This repository has been archived by the owner on Feb 19, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMarkdown.java
242 lines (224 loc) · 7.04 KB
/
Markdown.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
//
// Copyright 2016, Yahoo Inc.
// Copyrights licensed under the New BSD License.
// See the accompanying LICENSE file for terms.
//
// [![Build Status](https://travis-ci.org/jminusminus/markdown.svg?branch=master)](https://travis-ci.org/jminusminus/markdown)
// ## Stability: 0 - Unstable
// This package provides a simplified [Markdown](https://en.wikipedia.org/wiki/Markdown) text-to-HTML conversion tool.
package github.com.jminusminus.markdown;
import github.com.jminusminus.core.util.Map;
import github.com.jminusminus.core.util.LinkedListMap;
// ## Syntax
//
// ### Paragraphs
//
// A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines.
//
// ### Headers
//
// Headers use 1-6 hash characters at the start of the line, corresponding to header levels 1-6. For example:
//
// # This is an H1
// ## This is an H2
// ###### This is an H6
//
// ### Lists
//
// Unordered lists use asterisks, pluses, and hyphens interchangeably as list markers:
//
// * Red
// * Green
// * Blue
//
// is equivalent to:
//
// + Red
// + Green
// + Blue
//
// and:
//
// - Red
// - Green
// - Blue
//
// ### Code Blocks
//
// To produce a code block in Markdown, simply indent every line of the block by at least 4 spaces. For example, given this input:
//
// This is a normal paragraph:
//
// This is a code block.
//
// For pre-formatted code blocks specifing a langauge use three back tick quotes (```) on a line by them selfs.
//
// ```
// function foo() {
// return "bar";
// }
// ```
//
// Optional you can provide a language hint.
//
// ```javascript
// function foo() {
// return "bar";
// }
// ```
// ### Links
//
// To create an inline link, use a set of regular parentheses immediately after the link texts closing square bracket. Inside the parentheses, put the URL where you want the link to point. For example:
//
// This is [an example](http://example.com/) inline link.
//
// ### Emphasis
//
// Markdown treats underscores (_) as indicators of emphasis. Text wrapped with one (_) will be wrapped with an HTML <em> tag; double (_)'s will be wrapped with an HTML <strong> tag. E.g., this input:
//
// _single underscores_
//
// __double underscores__
//
// ### Code
//
// To indicate a span of code, wrap it with back tick quotes (`). Unlike a pre-formatted code block, a code span indicates code within a normal paragraph. For example:
//
// Use the `printf()` function.
//
// ### images
//
// Markdown uses an image syntax that is intended to resemble the syntax for links, allowing for two styles: inline and reference.
//
// Inline image syntax looks like this:
//
// ![Alt text](/path/to/img.jpg)
//
// Images can also be placed as link text.
//
public class Markdown {
// Line feed.
public static final String LF = "\n";
// Holds the found token elements.
protected Map elements = new LinkedListMap();
// The rendered HTML.
protected String html = "";
// Marks if a loop is in a paragraph.
protected boolean isParagraph = false;
// Marks if a loop is in a code block.
protected boolean isCode = false;
// Marks if a loop is in a list.
protected boolean isList = false;
// Returns HTML from the given Markdown string.
public static String parse(String str) {
Markdown md = new Markdown();
return md.parseStr(str).toString();
}
// Returns a Markdown instance with the given string parsed.
public Markdown parseStr(String str) {
this.tokenize(str);
return this;
}
// Returns HTML from the given Markdown.
public String toString() {
this.elements.forEach((element) -> {
String type = element.value().getClass().getName();
this.html += this.isParagraph(type);
this.html += this.isList(type);
this.html += this.isCode(type);
this.html += element.value().toString();
});
// Close out any remaining elements.
this.html += this.isParagraph("");
this.html += this.isList("");
this.html += this.isCode("");
return this.html;
}
protected String isParagraph(String type) {
if (this.isCode) {
return "";
}
if (!this.isParagraph && type.contains("Paragraph")) {
this.isParagraph = true;
return "<p>";
}
if (this.isParagraph && !type.contains("Paragraph")) {
this.isParagraph = false;
return "</p>" + Markdown.LF;
}
if (this.isParagraph) {
return " ";
}
return "";
}
protected String isList(String type) {
if (!this.isList && type.contains("List")) {
this.isList = true;
return "<ul>" + Markdown.LF;
}
if (this.isList && !type.contains("List")) {
this.isList = false;
return "</ul>" + Markdown.LF;
}
return "";
}
protected String isCode(String type) {
if (!this.isCode && type.contains("Code")) {
this.isCode = true;
return "<pre><code>";
}
if (this.isCode && !type.contains("Code")) {
this.isCode = false;
return "</code></pre>" + Markdown.LF;
}
return "";
}
protected int tokenize(String str) {
String[] lines = str.split(Markdown.LF);
int index = 0;
for (String line : lines) {
if (line.trim().length() > 0 || this.isCode) {
this.parseLine(index, line);
} else {
// Add empty space.
this.elements.put(index, "");
}
index++;
}
return index;
}
protected void parseLine(int index, String line) {
switch (line.charAt(0)) {
case '#': // Check for header
this.elements.put(index, new Title(line));
return;
case '*': // Check for list
case '+': // Check for list
case '-': // Check for list
this.elements.put(index, new List(line));
return;
default: // The line must be part of a block.
this.parseBlock(index, line);
}
}
protected void parseBlock(int index, String line) {
switch (line.charAt(0)) {
case ' ': // Check for code block
if (!this.isCode && " ".equals(line.substring(0, 4))) {
this.elements.put(index, new Code(line.substring(4)));
return;
}
case '`': // Check for code
if ("```".equals(line.substring(0, 3))) {
this.isCode = !this.isCode;
return;
}
default: // Check for paragraph
if (this.isCode) {
this.elements.put(index, new Code(line));
return;
}
this.elements.put(index, new Paragraph(line));
}
}
}