/**
* Copyright 2011 Oliver Buchtala
*
* This file is part of ndogen.
*
* ndogen is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ndogen is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ndogen. If not, see <http://www.gnu.org/licenses/>.
*/
package org.ndogen.converter;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.tree.Tree;
import org.ndogen.Fixture;
import org.ndogen.TestUtils;
import org.ndogen.antlr.DebugCharStream;
import org.ndogen.markdown.MarkdownParser;
import org.ndogen.markdown.MarkdownParser.document_return;
import org.ndogen.markdown.MarkdownScanner;
import org.ndogen.substance.Code;
import org.ndogen.substance.Creator;
import org.ndogen.substance.Document;
import org.ndogen.substance.NestedNode;
import org.ndogen.substance.Quote;
import org.ndogen.substance.Section;
import org.ndogen.substance.SubstanceNode;
import org.ndogen.substance.SubstanceUtils;
import org.ndogen.substance.Text;
import org.ndogen.util.FileUtils;
import org.ndogen.watch.task.ContentProvider;
public class Markdown2Substance implements ContentProvider {
ContentProvider markdownProvider;
String id = null;
String title = null;
String author = null;
Date createdAt = null;
Date modifiedAt = null;
public Markdown2Substance(ContentProvider markdownProvider) {
super();
this.markdownProvider = markdownProvider;
}
@Override
public String getContent() {
String input = markdownProvider.getContent();
CharStream lexerInput;
lexerInput = new ANTLRStringStream(input);
lexerInput = new DebugCharStream(lexerInput);
MarkdownScanner lexer = new MarkdownScanner(lexerInput);
CommonTokenStream tokens = new CommonTokenStream(lexer);
MarkdownParser parser = new MarkdownParser(tokens);
try {
document_return _doc = parser.document();
Tree tree = (Tree) _doc.getTree();
Document doc = new Document();
doc.setName(id);
doc.setCreator(author);
doc.setTitle(title);
doc.setCreated_at(createdAt);
doc.setUpdated_at(modifiedAt);
processTree(tree, new State(doc));
Map<String, SubstanceNode> nodeMap = new Creator(author, doc).create();
return FileUtils.objectToJson(nodeMap);
} catch (RecognitionException e) {
throw new RuntimeException(e);
}
}
private String extractHeader(String key, String text) {
int colonIdx = text.indexOf(":");
if(colonIdx<0) {
throw new RuntimeException("Illegal header line: " + text);
}
return text.substring(colonIdx).trim();
}
protected void processTree(Tree node, State state) {
int type = node.getType();
switch(type) {
case 0:
// if id and title are not set they have to be
// specified in the first paragraph
boolean firstIsHeader = false;
if( id==null
|| title ==null
|| author == null
|| createdAt == null) {
firstIsHeader = true;
Tree header = node.getChild(0);
for (int i = 0; i < header.getChildCount(); i++) {
String text = header.getChild(i).getText();
if(author == null && text.toLowerCase().startsWith("author")) {
author = extractHeader("author", text);
}
else if(id == null && text.toLowerCase().startsWith("id")) {
id = extractHeader("id", text);
}
else if(title == null && text.toLowerCase().startsWith("title")) {
title = extractHeader("title", text);
}
else if(title == null && text.toLowerCase().startsWith("createAt")) {
String _createdAt = extractHeader("createAt", text);
try {
createdAt = new SimpleDateFormat().parse(_createdAt);
} catch (ParseException e) {
System.err.println("Could not parse createdAt date :" + createdAt);
e.printStackTrace();
}
}
}
}
if(id==null
|| title ==null
|| author == null
|| createdAt == null
|| modifiedAt == null) {
throw new RuntimeException("Meta information must be set on command line or in a header paragraph of document");
}
Document doc = (Document) state.container;
doc.setName(id);
doc.setCreator(author);
doc.setTitle(title);
doc.setCreated_at(createdAt);
doc.setUpdated_at(modifiedAt);
for (int i = (firstIsHeader?1:0); i < node.getChildCount(); i++) {
processTree(node.getChild(i), state);
}
break;
case MarkdownParser.SECTION:
{
Section section = new Section();
String title = node.getChild(0).getText();
section.setName(title);
State _state = new State(section);
for (int i = 1; i < node.getChildCount(); i++) {
processTree(node.getChild(i), _state);
}
state.container.addChild(section);
break;
}
case MarkdownParser.TEXT_BLOCK:
{
Text text = new Text();
StringWriter sw = new StringWriter();
SimpleWriter out = new SimpleWriter(new PrintWriter(sw));
for (int i = 0; i < node.getChildCount(); i++) {
processText(out, node.getChild(i), state);
}
text.setContent(sw.toString());
state.container.addChild(text);
break;
}
case MarkdownParser.LIST:
{
Text text = new Text();
StringWriter sw = new StringWriter();
SimpleWriter out = new SimpleWriter(new PrintWriter(sw));
processText(out, node, state);
text.setContent(sw.toString());
state.container.addChild(text);
break;
}
case MarkdownParser.QUOTE_BLOCK:
{
Quote quote = new Quote();
StringWriter sw = new StringWriter();
SimpleWriter out = new SimpleWriter(new PrintWriter(sw));
State _state = new State();
_state.breakLine = true;
for (int i = 0; i < node.getChildCount(); i++) {
processText(out, node.getChild(i), _state);
}
quote.setContent(sw.toString());
state.container.addChild(quote);
break;
}
case MarkdownParser.CODE_BLOCK:
{
Code code = new Code();
StringWriter sw = new StringWriter();
SimpleWriter out = new SimpleWriter(new PrintWriter(sw));
State _state = new State();
_state.insertNL = true;
for (int i = 0; i < node.getChildCount(); i++) {
processText(out, node.getChild(i), _state);
}
code.setContent(sw.toString());
state.container.addChild(code);
break;
}
default:
System.out.println("Skipping unhandled node: type="+ node.getType() + ", text=" + node.getText());
// skip
}
}
protected void processText(SimpleWriter out, Tree node, State state) {
int type = node.getType();
switch(type) {
case MarkdownParser.NL:
if(state.breakLine) {
out.println("<br/>");
} else if (state.insertNL) {
out.println();
} else {
out.print(" ");
}
break;
case MarkdownParser.EL:
// skip this
break;
case MarkdownParser.TEXT:
out.print(node.getText());
break;
case MarkdownParser.LIST:
out.println("<ul>");
for (int i = 0; i < node.getChildCount(); i++) {
processText(out, node.getChild(i), state);
}
out.println("</ul>");
break;
case MarkdownParser.LIST_ITEM:
out.println("<li>");
for (int i = 0; i < node.getChildCount(); i++) {
processText(out, node.getChild(i), state);
}
out.println();
out.println("</li>");
break;
case MarkdownParser.EMPH:
out.print("<em>");
for (int i = 0; i < node.getChildCount(); i++) {
processText(out, node.getChild(i), state);
}
out.print("</em>");
break;
case MarkdownParser.STRONG:
out.print("<strong>");
for (int i = 0; i < node.getChildCount(); i++) {
processText(out, node.getChild(i), state);
}
out.print("</strong>");
break;
case MarkdownParser.INLINE_CODE:
out.print("<code>");
for (int i = 0; i < node.getChildCount(); i++) {
processText(out, node.getChild(i), state);
}
out.print("</code>");
break;
case MarkdownParser.LINK:
String text = node.getChild(0).getText();
String link = node.getChild(1).getText();
try {
URL url = new URL(link);
link = url.toString();
} catch (MalformedURLException e) {
System.err.println("Malformed Url: " + link);
out.print(text);
break;
}
out.print("<a href=\""+link+"\">"+text+"</a>");
break;
default:
System.out.println("Skipping unhandled node: type="+ node.getType() + ", text=" + node.getText());
// skip
}
}
public static class State {
NestedNode container = null;
boolean insertNL = false;
boolean breakLine = false;
public State() {
}
public State(NestedNode container) {
this.container = container;
}
}
public static void main(String[] args) throws IOException {
// String fixtureId = "two_sections";
// Fixture fixture = TestUtils.getFixture("fixtures/markdown/parser", fixtureId);
// File file = new File("fixtures/markdown/parser/"+fixtureId);
// final String input = fixture.getInput();
String _file = "examples/Example1.markdown";
File file = new File(_file);
String name = "Example1";
final String input = FileUtils.readFile(_file);
Markdown2Substance markdown2Substance = new Markdown2Substance(new ContentProvider() {
@Override
public String getContent() {
return input;
}
});
markdown2Substance.id = name;
markdown2Substance.title = "Test: " + name;
markdown2Substance.author = "oliver";
markdown2Substance.createdAt = new Date(file.lastModified());
markdown2Substance.modifiedAt = new Date(file.lastModified());
String substanceJson = markdown2Substance.getContent();
SubstanceUtils.push(substanceJson);
}
}