
org.pegdown.ToHtmlSerializer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pegdown Show documentation
Show all versions of pegdown Show documentation
A Java 1.5+ library providing a clean and lightweight markdown processor
/*
* 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 java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;
import static org.parboiled.common.Preconditions.checkArgNotNull;
public class ToHtmlSerializer implements Visitor, Printer.Encoder {
protected Printer printer = new Printer();
protected final Map references = new HashMap();
protected final Map abbreviations = new HashMap();
protected TableNode currentTableNode;
protected int currentTableColumn;
protected boolean inTableHeader;
protected Random random = new Random(0x2626); // for email obfuscation
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(AutoLinkNode node) {
printer.print("" : "\">")
.printEncoded(node.getText(), this)
.print("");
}
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) {
printTag(node, "dd");
}
public void visit(DefinitionTermNode node) {
printTag(node, "dt");
}
public void visit(EmphNode node) {
printTag(node, "em");
}
public void visit(ExpLinkNode node) {
if (node.getImage()) {
printer.print("
");
} else {
printer.print("');
visitChildren(node);
printer.print("");
}
}
public void visit(HeaderNode node) {
printTag(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) {
printer.println();
printTag(node, "li");
}
public void visit(MailLinkNode node) {
printer.print("")
.print(obfuscate(node.getText()))
.print("");
}
public void visit(OrderedListNode node) {
printIndentedTag(node, "ol");
}
public void visit(ParaNode node) {
printTag(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(final RefLinkNode node) {
String key = printToString(new Runnable() {
public void run() {
SuperNode keyNode = node.getReferenceKey() != null ? node.getReferenceKey() : node;
visitChildren(keyNode);
}
});
ReferenceNode refNode = key != null ? references.get(normalize(key)) : null;
if (refNode == null) {
// "fake" reference link
printer.print('[');
visitChildren(node);
printer.print(']');
if (node.getSeparatorSpace() != null) {
printer.print(node.getSeparatorSpace()).print('[');
if (node.getReferenceKey() != null) node.getReferenceKey().accept(this);
printer.print(']');
}
return;
}
if (node.getImage()) {
printer.print("
");
} else {
printer.print("');
visitChildren(node);
printer.print("");
}
}
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(StrongNode node) {
printTag(node, "strong");
}
public void visit(TableBodyNode node) {
printIndentedTag(node, "tbody");
}
public void visit(TableCellNode node) {
String tag = inTableHeader ? "th" : "td";
TableColumnNode column = currentTableNode.getColumns().get(currentTableColumn);
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) {
printer.println().print("");
String text = node.getText();
text = transformVerbatimText(text);
printer.printEncoded(text, this);
printer.print("
");
}
public String transformVerbatimText(String text) {
// transform all initial newlines to HTML breaks
while(text.charAt(0) == '\n') {
printer.print("
");
text = text.substring(1);
}
return text;
}
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(), this);
}
public void visit(SuperNode node) {
visitChildren(node);
}
public void visit(Node node) {
// override this method for processing custom Node implementations
throw new RuntimeException("Not implemented");
}
// Printer.Encoder
public String encode(char c) {
switch (c) {
case '&':
return "&";
case '<':
return "<";
case '>':
return ">";
case '"':
return """;
case '\'':
return "'";
}
return null;
}
// helpers
protected void visitChildren(SuperNode node) {
for (Node child : node.getChildren()) {
child.accept(this);
}
}
protected void printTag(TextNode node, String tag) {
printer
.print('<').print(tag).print('>')
.printEncoded(node.getText(), this)
.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 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 String printToString(Runnable runnable) {
Printer priorPrinter = printer;
printer = new Printer();
runnable.run();
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 String obfuscate(String email) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < email.length(); i++) {
char c = email.charAt(i);
switch (random.nextInt(5)) {
case 0:
case 1:
sb.append("").append((int) c).append(';');
break;
case 2:
case 3:
sb.append("").append(Integer.toHexString(c)).append(';');
break;
case 4:
String encoded = encode(c);
if (encoded != null) sb.append(encoded); else sb.append(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), this);
printer.print("');
printer.printEncoded(abbr, this);
printer.print("");
ix = sx + abbr.length();
}
printer.print(string.substring(ix));
} else {
printer.print(string);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy