All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.pegdown.ToHtmlSerializer Maven / Gradle / Ivy

/*
 * Copyright (C) 2010-2011 Mathias Doenitz
 *
 * Based on peg-markdown (C) 2008-2010 John MacFarlane
 *
 * 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 org.pegdown;

import org.parboiled.common.StringUtils;
import org.pegdown.ast.*;
import org.pegdown.plugins.ToHtmlSerializerPlugin;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import static org.parboiled.common.Preconditions.checkArgNotNull;

public class ToHtmlSerializer implements Visitor {

    protected Printer printer = new Printer();
    protected final Map references = new HashMap();
    protected final Map abbreviations = new HashMap();
    protected final LinkRenderer linkRenderer;
    protected final List plugins;

    protected TableNode currentTableNode;
    protected int currentTableColumn;
    protected boolean inTableHeader;

    protected Map verbatimSerializers;

    public ToHtmlSerializer(LinkRenderer linkRenderer) {
        this(linkRenderer, Collections.emptyList());
    }

    public ToHtmlSerializer(LinkRenderer linkRenderer, List plugins) {
        this(linkRenderer, Collections.emptyMap(), plugins);
    }

    public ToHtmlSerializer(final LinkRenderer linkRenderer, final Map verbatimSerializers) {
        this(linkRenderer, verbatimSerializers, Collections.emptyList());
    }

    public ToHtmlSerializer(final LinkRenderer linkRenderer, final Map verbatimSerializers, final List plugins) {
        this.linkRenderer = linkRenderer;
        this.verbatimSerializers = new HashMap(verbatimSerializers);
        if (!this.verbatimSerializers.containsKey(VerbatimSerializer.DEFAULT)) {
            this.verbatimSerializers.put(VerbatimSerializer.DEFAULT, DefaultVerbatimSerializer.INSTANCE);
        }
        this.plugins = plugins;
    }

    public String toHtml(RootNode astRoot) {
        checkArgNotNull(astRoot, "astRoot");
        astRoot.accept(this);
        return printer.getString();
    }

    public void visit(RootNode node) {
        for (ReferenceNode refNode : node.getReferences()) {
            visitChildren(refNode);
            references.put(normalize(printer.getString()), refNode);
            printer.clear();
        }
        for (AbbreviationNode abbrNode : node.getAbbreviations()) {
            visitChildren(abbrNode);
            String abbr = printer.getString();
            printer.clear();
            abbrNode.getExpansion().accept(this);
            String expansion = printer.getString();
            abbreviations.put(abbr, expansion);
            printer.clear();
        }
        visitChildren(node);
    }

    public void visit(AbbreviationNode node) {
    }

    public void visit(AnchorLinkNode node) {
        printLink(linkRenderer.render(node));
    }

    public void visit(AutoLinkNode node) {
        printLink(linkRenderer.render(node));
    }

    public void visit(BlockQuoteNode node) {
        printIndentedTag(node, "blockquote");
    }

    public void visit(BulletListNode node) {
        printIndentedTag(node, "ul");
    }

    public void visit(CodeNode node) {
        printTag(node, "code");
    }

    public void visit(DefinitionListNode node) {
        printIndentedTag(node, "dl");
    }

    public void visit(DefinitionNode node) {
        printConditionallyIndentedTag(node, "dd");
    }

    public void visit(DefinitionTermNode node) {
        printConditionallyIndentedTag(node, "dt");
    }

    public void visit(ExpImageNode node) {
        String text = printChildrenToString(node);
        printImageTag(linkRenderer.render(node, text));
    }

    public void visit(ExpLinkNode node) {
        String text = printChildrenToString(node);
        printLink(linkRenderer.render(node, text));
    }

    public void visit(HeaderNode node) {
        printBreakBeforeTag(node, "h" + node.getLevel());
    }

    public void visit(HtmlBlockNode node) {
        String text = node.getText();
        if (text.length() > 0) printer.println();
        printer.print(text);
    }

    public void visit(InlineHtmlNode node) {
        printer.print(node.getText());
    }

    public void visit(ListItemNode node) {
        if (node instanceof TaskListNode) {
            // vsch: #185 handle GitHub style task list items, these are a bit messy because the  checkbox needs to be
            // included inside the optional 

first grand-child of the list item, first child is always RootNode // because the list item text is recursively parsed. Node firstChild = node.getChildren().get(0).getChildren().get(0); boolean firstIsPara = firstChild instanceof ParaNode; int indent = node.getChildren().size() > 1 ? 2 : 0; boolean startWasNewLine = printer.endsWithNewLine(); printer.println().print("
  • ").indent(indent); if (firstIsPara) { printer.println().print("

    "); printer.print(""); visitChildren((SuperNode) firstChild); // render the other children, the p tag is taken care of here visitChildrenSkipFirst(node); printer.print("

    "); } else { printer.print(""); visitChildren(node); } printer.indent(-indent).printchkln(indent != 0).print("
  • ") .printchkln(startWasNewLine); } else { printConditionallyIndentedTag(node, "li"); } } public void visit(MailLinkNode node) { printLink(linkRenderer.render(node)); } public void visit(OrderedListNode node) { printIndentedTag(node, "ol"); } public void visit(ParaNode node) { printBreakBeforeTag(node, "p"); } public void visit(QuotedNode node) { switch (node.getType()) { case DoubleAngle: printer.print("«"); visitChildren(node); printer.print("»"); break; case Double: printer.print("“"); visitChildren(node); printer.print("”"); break; case Single: printer.print("‘"); visitChildren(node); printer.print("’"); break; } } public void visit(ReferenceNode node) { // reference nodes are not printed } public void visit(RefImageNode node) { String text = printChildrenToString(node); String key = node.referenceKey != null ? printChildrenToString(node.referenceKey) : text; ReferenceNode refNode = references.get(normalize(key)); if (refNode == null) { // "fake" reference image link printer.print("![").print(text).print(']'); if (node.separatorSpace != null) { printer.print(node.separatorSpace).print('['); if (node.referenceKey != null) printer.print(key); printer.print(']'); } } else printImageTag(linkRenderer.render(node, refNode.getUrl(), refNode.getTitle(), text)); } public void visit(RefLinkNode node) { String text = printChildrenToString(node); String key = node.referenceKey != null ? printChildrenToString(node.referenceKey) : text; ReferenceNode refNode = references.get(normalize(key)); if (refNode == null) { // "fake" reference link printer.print('[').print(text).print(']'); if (node.separatorSpace != null) { printer.print(node.separatorSpace).print('['); if (node.referenceKey != null) printer.print(key); printer.print(']'); } } else printLink(linkRenderer.render(node, refNode.getUrl(), refNode.getTitle(), text)); } public void visit(SimpleNode node) { switch (node.getType()) { case Apostrophe: printer.print("’"); break; case Ellipsis: printer.print("…"); break; case Emdash: printer.print("—"); break; case Endash: printer.print("–"); break; case HRule: printer.println().print("
    "); break; case Linebreak: printer.print("
    "); break; case Nbsp: printer.print(" "); break; default: throw new IllegalStateException(); } } public void visit(StrongEmphSuperNode node) { if (node.isClosed()) { if (node.isStrong()) printTag(node, "strong"); else printTag(node, "em"); } else { //sequence was not closed, treat open chars as ordinary chars printer.print(node.getChars()); visitChildren(node); } } public void visit(StrikeNode node) { printTag(node, "del"); } public void visit(TableBodyNode node) { printIndentedTag(node, "tbody"); } @Override public void visit(TableCaptionNode node) { printer.println().print(""); visitChildren(node); printer.print(""); } public void visit(TableCellNode node) { String tag = inTableHeader ? "th" : "td"; List columns = currentTableNode.getColumns(); TableColumnNode column = columns.get(Math.min(currentTableColumn, columns.size() - 1)); printer.println().print('<').print(tag); column.accept(this); if (node.getColSpan() > 1) printer.print(" colspan=\"").print(Integer.toString(node.getColSpan())).print('"'); printer.print('>'); visitChildren(node); printer.print('<').print('/').print(tag).print('>'); currentTableColumn += node.getColSpan(); } public void visit(TableColumnNode node) { switch (node.getAlignment()) { case None: break; case Left: printer.print(" align=\"left\""); break; case Right: printer.print(" align=\"right\""); break; case Center: printer.print(" align=\"center\""); break; default: throw new IllegalStateException(); } } public void visit(TableHeaderNode node) { inTableHeader = true; printIndentedTag(node, "thead"); inTableHeader = false; } public void visit(TableNode node) { currentTableNode = node; printIndentedTag(node, "table"); currentTableNode = null; } public void visit(TableRowNode node) { currentTableColumn = 0; printIndentedTag(node, "tr"); } public void visit(VerbatimNode node) { VerbatimSerializer serializer = lookupSerializer(node.getType()); serializer.serialize(node, printer); } protected VerbatimSerializer lookupSerializer(final String type) { if (type != null && verbatimSerializers.containsKey(type)) { return verbatimSerializers.get(type); } else { return verbatimSerializers.get(VerbatimSerializer.DEFAULT); } } public void visit(WikiLinkNode node) { printLink(linkRenderer.render(node)); } public void visit(TextNode node) { if (abbreviations.isEmpty()) { printer.print(node.getText()); } else { printWithAbbreviations(node.getText()); } } public void visit(SpecialTextNode node) { printer.printEncoded(node.getText()); } public void visit(SuperNode node) { visitChildren(node); } public void visit(Node node) { for (ToHtmlSerializerPlugin plugin : plugins) { if (plugin.visit(node, this, printer)) { return; } } // override this method for processing custom Node implementations throw new RuntimeException("Don't know how to handle node " + node); } // helpers protected void visitChildren(SuperNode node) { for (Node child : node.getChildren()) { child.accept(this); } } // helpers protected void visitChildrenSkipFirst(SuperNode node) { boolean first = true; for (Node child : node.getChildren()) { if (!first) child.accept(this); first = false; } } protected void printTag(TextNode node, String tag) { printer.print('<').print(tag).print('>'); printer.printEncoded(node.getText()); printer.print('<').print('/').print(tag).print('>'); } protected void printTag(SuperNode node, String tag) { printer.print('<').print(tag).print('>'); visitChildren(node); printer.print('<').print('/').print(tag).print('>'); } protected void printBreakBeforeTag(SuperNode node, String tag) { boolean startWasNewLine = printer.endsWithNewLine(); printer.println(); printTag(node, tag); if (startWasNewLine) printer.println(); } protected void printIndentedTag(SuperNode node, String tag) { printer.println().print('<').print(tag).print('>').indent(+2); visitChildren(node); printer.indent(-2).println().print('<').print('/').print(tag).print('>'); } protected void printConditionallyIndentedTag(SuperNode node, String tag) { if (node.getChildren().size() > 1) { printer.println().print('<').print(tag).print('>').indent(+2); visitChildren(node); printer.indent(-2).println().print('<').print('/').print(tag).print('>'); } else { boolean startWasNewLine = printer.endsWithNewLine(); printer.println().print('<').print(tag).print('>'); visitChildren(node); printer.print('<').print('/').print(tag).print('>').printchkln(startWasNewLine); } } protected void printImageTag(LinkRenderer.Rendering rendering) { printer.print(""); } protected void printLink(LinkRenderer.Rendering rendering) { printer.print('<').print('a'); printAttribute("href", rendering.href); for (LinkRenderer.Attribute attr : rendering.attributes) { printAttribute(attr.name, attr.value); } printer.print('>').print(rendering.text).print(""); } protected void printAttribute(String name, String value) { printer.print(' ').print(name).print('=').print('"').print(value).print('"'); } protected String printChildrenToString(SuperNode node) { Printer priorPrinter = printer; printer = new Printer(); visitChildren(node); String result = printer.getString(); printer = priorPrinter; return result; } protected String normalize(String string) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < string.length(); i++) { char c = string.charAt(i); switch (c) { case ' ': case '\n': case '\t': continue; } sb.append(Character.toLowerCase(c)); } return sb.toString(); } protected void printWithAbbreviations(String string) { Map> expansions = null; for (Map.Entry entry : abbreviations.entrySet()) { // first check, whether we have a legal match String abbr = entry.getKey(); int ix = 0; while (true) { int sx = string.indexOf(abbr, ix); if (sx == -1) break; // only allow whole word matches ix = sx + abbr.length(); if (sx > 0 && Character.isLetterOrDigit(string.charAt(sx - 1))) continue; if (ix < string.length() && Character.isLetterOrDigit(string.charAt(ix))) { continue; } // ok, legal match so save an expansions "task" for all matches if (expansions == null) { expansions = new TreeMap>(); } expansions.put(sx, entry); } } if (expansions != null) { int ix = 0; for (Map.Entry> entry : expansions.entrySet()) { int sx = entry.getKey(); String abbr = entry.getValue().getKey(); String expansion = entry.getValue().getValue(); printer.printEncoded(string.substring(ix, sx)); printer.print("'); printer.printEncoded(abbr); printer.print(""); ix = sx + abbr.length(); } printer.print(string.substring(ix)); } else { printer.print(string); } } }




    © 2015 - 2024 Weber Informatics LLC | Privacy Policy