org.eclipse.scout.docs.publish.PublishHelpUtility.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.scout.docs.publish.PublishHelpUtility.java

Source

/*******************************************************************************
 * Copyright (c) 2015 BSI Business Systems Integration AG.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Distribution License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/org/documents/edl-v10.html
 *
 * Contributors:
 *     BSI Business Systems Integration AG - initial API and implementation
 ******************************************************************************/
package org.eclipse.scout.docs.publish;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.mylyn.wikitext.core.parser.outline.OutlineItem;
import org.eclipse.mylyn.wikitext.core.parser.util.MarkupToEclipseToc;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.parser.Tag;
import org.jsoup.select.Elements;

import com.google.common.base.Charsets;
import com.google.common.io.Files;

/**
 *
 */
public class PublishHelpUtility {

    private static final String IMAGES_SUB_PATH = "images/";
    private static final String HTML_SUB_PATH = "html/";
    private static final String IMAGE_HOME = "home.gif";
    private static final String IMAGE_NEXT = "next.gif";
    private static final String IMAGE_PREV = "prev.gif";

    public static void publishEclipseHelp(File rootInFolder, File rootOutFolder, File listOfPagesFile)
            throws IOException {
        if (!rootInFolder.exists() || !rootInFolder.isDirectory()) {
            throw new IllegalStateException(
                    "Folder rootInFolder '" + rootInFolder.getAbsolutePath() + "' not found.");
        }

        if (!listOfPagesFile.exists() || !listOfPagesFile.isFile()) {
            throw new IllegalStateException(
                    "File listOfPagesFile '" + listOfPagesFile.getAbsolutePath() + "' not found.");
        }

        List<String> pages = Files.readLines(listOfPagesFile, Charsets.ISO_8859_1);

        List<File> inFiles = new ArrayList<File>();
        for (String p : pages) {
            if (p != null && p.length() > 0) {
                File f = new File(rootInFolder, p);
                if (!f.exists() || !f.isFile()) {
                    throw new IllegalStateException("File '" + f.getAbsolutePath() + "' not found.");
                }
                inFiles.add(f);
            }
        }

        OutlineItemEx root = new OutlineItemEx(null, 0, "id", 0, 0, "Eclipse Scout User Guide");
        root.setFilePath(HTML_SUB_PATH + inFiles.get(0).getName());

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //Check if all files from the "rootInFolder" are contained in the inFiles list

        List<File> filesInFolder = new ArrayList<File>();
        filesInFolder.addAll(Arrays.asList(rootInFolder.listFiles(new FileFilter() {

            @Override
            public boolean accept(File f) {
                return f.getName().endsWith("html");
            }
        })));

        for (File f : filesInFolder) {
            if (!inFiles.contains(f)) {
                throw new IllegalStateException("File '" + f.getName() + "' is missing in the inFiles list");
            }
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////

        File htmlFolder = new File(rootOutFolder, HTML_SUB_PATH);
        if (htmlFolder.exists()) {
            htmlFolder.delete();
        }

        copyNavImg(htmlFolder);

        Map<Integer, OutlineItemEx> nodeMap = new HashMap<Integer, OutlineItemEx>();
        putNode(nodeMap, root, root.getLevel());
        for (int i = 0; i < inFiles.size(); i++) {
            File inFile = inFiles.get(i);
            File outFile = new File(htmlFolder, inFile.getName());

            String html = Files.toString(inFile, Charsets.ISO_8859_1);
            String filePath = outFile.getAbsolutePath().substring(rootOutFolder.getAbsolutePath().length() + 1)
                    .replaceAll("\\\\", "/");
            Document doc = Jsoup.parse(html);
            doc.outputSettings().charset("ASCII");

            if (i > 0) {
                //Next files are taken into account in the outline tree:
                computeOutlineNodes(nodeMap, doc, filePath);
            }

            //Create the navigation section:
            String nextHref = null;
            String nextTitle = null;
            String prevHref = null;
            String prevTitle = null;

            if (i < inFiles.size() - 1) {
                nextHref = inFiles.get(i + 1).getName();
                nextTitle = readAndFindFirstHeader(inFiles.get(i + 1));
            }
            if (i > 0 && inFiles.size() > 0) {
                prevHref = inFiles.get(i - 1).getName();
                prevTitle = readAndFindFirstHeader(inFiles.get(i - 1));
            }
            String baseUri = doc.baseUri();

            String title = findFirstHeader(doc);
            Element tableTop = createNavigationTable(root, title, true, nextHref, prevHref, nextTitle, prevTitle,
                    baseUri);
            doc.body().insertChildren(0, Collections.singleton(tableTop));
            Element tableBottom = createNavigationTable(root, title, false, nextHref, prevHref, nextTitle,
                    prevTitle, baseUri);
            insertBeforeId(doc.body(), "footer", tableBottom);

            //Move and Copy Images:
            PublishUtility.moveAndcopyImages(doc, inFile.getParentFile(), htmlFolder, IMAGES_SUB_PATH);

            //Move and Copy CSS:
            PublishUtility.moveAndcopyCss(doc, inFile.getParentFile(), htmlFolder, "css/");

            //Fix Figure Links:
            PublishUtility.fixListingLink(doc);
            PublishUtility.fixFigureLink(doc);

            //Write as file:
            Files.createParentDirs(outFile);
            Files.write(doc.toString(), outFile, Charsets.ISO_8859_1);
        }

        MarkupToEclipseToc eclipseToc = new MarkupToEclipseToc() {
            @Override
            protected String computeFile(OutlineItem item) {
                if (item instanceof OutlineItemEx && ((OutlineItemEx) item).getFilePath() != null) {
                    return ((OutlineItemEx) item).getFilePath();
                }
                return super.computeFile(item);
            }
        };
        eclipseToc.setBookTitle(root.getLabel());
        eclipseToc.setHtmlFile(root.getFilePath());
        String tocContent = eclipseToc.createToc(root);
        File outTocFile = new File(rootOutFolder, "toc.xml");
        Files.write(tocContent, outTocFile, Charsets.ISO_8859_1);
    }

    private static void insertBeforeId(Element container, String id, Element element) {
        for (int i = 0; i < container.childNodeSize(); i++) {
            Node childNode = container.childNode(i);
            if (id.equals(childNode.attr("id"))) {
                container.insertChildren(i, Collections.singleton(element));
                return;
            }
        }
        throw new IllegalStateException("ChildNode with id '" + id + "' not found");
    }

    private static Element createNavigationTable(OutlineItemEx root, String title, boolean isTop, String nextHref,
            String prevHref, String nextTitle, String prevTitle, String baseUri) {
        Element table = new Element(Tag.valueOf("table"), baseUri);
        table.attr("border", "0");
        table.attr("class", "navigation");
        table.attr("style", "width: 100%;");
        table.attr("summary", "navigation");

        Element prevLinkElement = createLinkElement(prevTitle, prevHref, "Previous", IMAGE_PREV, baseUri);
        Element nextLinkElement = createLinkElement(nextTitle, nextHref, "Next", IMAGE_NEXT, baseUri);

        Element homeLinkElement;
        if (isTop) {
            homeLinkElement = null;
        } else {
            homeLinkElement = createLinkElement(root.getLabel(),
                    root.getFilePath().substring(HTML_SUB_PATH.length()), root.getLabel(), IMAGE_HOME,
                    table.baseUri());
        }
        appendNavigationTableTR(table, prevLinkElement, homeLinkElement, nextLinkElement);
        appendNavigationTableTR(table, prevTitle, null, nextTitle);
        return table;
    }

    /**
     * @param title
     * @param href
     * @param imgAlt
     * @param imgSrc
     * @param baseUri
     * @return
     */
    private static Element createLinkElement(String title, String href, String imgAlt, String imgSrc,
            String baseUri) {
        if (title == null || href == null) {
            return null;
        }
        Element a = new Element(Tag.valueOf("a"), baseUri);
        a.attr("href", href);
        a.attr("shape", "rect");
        a.attr("title", title);
        Element img = new Element(Tag.valueOf("img"), baseUri);
        img.attr("alt", imgAlt);
        img.attr("border", "0");
        img.attr("src", imgSrc);
        a.appendChild(img);
        return a;
    }

    private static void appendNavigationTableTR(Element table, Object leftContent, Object midContent,
            Object rigthContent) {
        Element tr = new Element(Tag.valueOf("tr"), table.baseUri());
        table.appendChild(tr);
        appendNavigationTableTD(tr, "left", "width: 30%", leftContent);
        appendNavigationTableTD(tr, "center", "width: 40%", midContent);
        appendNavigationTableTD(tr, "right", "width: 30%", rigthContent);
    }

    private static void appendNavigationTableTD(Element tr, String align, String style, Object content) {
        Element td = new Element(Tag.valueOf("td"), tr.baseUri());
        td.attr("align", align);
        td.attr("colspan", "1");
        td.attr("rowspan", "1");
        td.attr("style", style);
        tr.appendChild(td);
        if (content == null) {
            //Do nothing
        } else if (content instanceof String) {
            td.text((String) content);
        } else if (content instanceof Element) {
            td.appendChild((Node) content);
        } else {
            System.err.println("Unexpected content type: " + content);
        }
    }

    private static void computeOutlineNodes(Map<Integer, OutlineItemEx> nodeMap, Document doc, String filePath) {
        Elements elements = doc.getAllElements();
        for (Element element : elements) {
            if (isHeaderTag(element)) {
                String id = findId(element);
                if (id == null) {
                    System.err.println(
                            "id is not found for node " + element.nodeName() + " '" + element.text() + "'");
                }
                int level = Integer.parseInt(element.nodeName().substring(1));
                String title = sanitize(element.text());
                OutlineItem parent = findParent(nodeMap, level);
                OutlineItemEx node = new OutlineItemEx(parent, level, id, 0, 0, title);
                node.setFilePath(filePath);
                putNode(nodeMap, node, level);
            }
        }
    }

    private static boolean isHeaderTag(Element element) {
        return element.nodeName().matches("h[1-6]");
    }

    private static String sanitize(String text) {
        String result = text;
        result = result.replaceAll("", "\"");
        return result;
    }

    /**
     * Put the node in
     *
     * @param nodeMap
     *          the map containing the last known node for each level
     * @param node
     *          the node that needs to be added
     * @param level
     *          the level (1 for h1, 2 for h2 ...)
     */
    private static void putNode(Map<Integer, OutlineItemEx> nodeMap, OutlineItemEx node, int level) {
        nodeMap.put(level, node);
    }

    /**
     * Find the parent node given a specific level.
     *
     * @param nodeMap
     *          the map containing the last known node for each level
     * @param level
     *          the level of the current node
     * @return parentNode
     */
    private static OutlineItem findParent(Map<Integer, OutlineItemEx> nodeMap, int level) {
        int i = level - 1;
        while (nodeMap.get(i) == null && i > 0) {
            i = i - 1;
        }
        return nodeMap.get(i);
    }

    /**
     * Find the id of a header tag. id is defined as id attribute of the header, or as id attribute of a nested "a" tag
     *
     * @param element
     *          element corresponding to the HTML header tag (h1, h2, h3, h4, h5 or h6)
     * @return id
     */
    private static String findId(Element element) {
        String id = findIdForElement(element);
        if (id == null) {
            Elements childElements = element.getElementsByTag("a");
            int i = 0;
            while (id == null && i < childElements.size()) {
                Element childElement = childElements.get(i);
                id = findIdForElement(childElement);
                i = i + 1;
            }
        }
        return id;
    }

    /**
     * Find the Id of a given element.
     *
     * @param element
     * @return id
     */
    private static String findIdForElement(Element element) {
        if (element.id() != null && element.id().length() > 0) {
            return element.id();
        }
        return null;
    }

    /**
     * @param htmlFolder
     * @throws IOException
     */
    private static void copyNavImg(File htmlFolder) throws IOException {
        File fromFolder = new File("nav_images");
        File toFolder = new File(htmlFolder, IMAGES_SUB_PATH);
        toFolder.mkdirs();
        Files.copy(new File(fromFolder, IMAGE_HOME), new File(toFolder, IMAGE_HOME));
        Files.copy(new File(fromFolder, IMAGE_NEXT), new File(toFolder, IMAGE_NEXT));
        Files.copy(new File(fromFolder, IMAGE_PREV), new File(toFolder, IMAGE_PREV));
    }

    /**
     * Read the file and apply {@link #findFirstHeader(Document)}
     *
     * @param file
     * @return title or null if not found
     * @throws IOException
     */
    private static String readAndFindFirstHeader(File file) throws IOException {
        String html = Files.toString(file, Charsets.ISO_8859_1);
        Document doc = Jsoup.parse(html);
        return findFirstHeader(doc);
    }

    /**
     * Find the first header (h1, h2, h3, h4, h5 or h6) and returns the text content .
     *
     * @param doc
     *          the html content as JSoup document
     * @return title or null if not found
     */
    private static String findFirstHeader(Document doc) {
        Elements elements = doc.getAllElements();
        for (Element element : elements) {
            if (isHeaderTag(element)) {
                return sanitize(element.text());
            }
        }
        return null;
    }
}