Files
prog-intro/java/md2html/PrimePartCreator.java

391 lines
12 KiB
Java

package md2html;
import java.util.ArrayList;
import java.util.List;
import markup.*;
public class PrimePartCreator {
private final String block;
private int currentChar;
public enum MarkdownToken {
WORD,
EMPHASIS_STAR,
STRONG_STAR,
EMPHASIS_UNDERLINE,
STRONG_UNDERLINE,
STRIKEOUT,
CODE,
SCREENING,
VAR,
SAMP,
INS,
DEL,
PRE,
NOTHING,
}
public PrimePartCreator(String block) {
this.block = block;
currentChar = 0;
}
public PrimePart createPart() {
PrimePart result;
if (isHeader()) {
int levelOfHeader = 0;
while (
levelOfHeader < block.length() &&
block.charAt(levelOfHeader) == '#'
) {
levelOfHeader++;
}
currentChar = levelOfHeader + 1;
result = new Header(buildPart(MarkdownToken.WORD), levelOfHeader);
} else {
currentChar = 0;
result = new Paragraph(buildPart(MarkdownToken.WORD));
}
return result;
}
private List<PartOfParagraph> buildPart(MarkdownToken currentToken) {
List<PartOfParagraph> items = new ArrayList<>();
MarkdownToken token = nextMarkdownToken();
StringBuilder sb = new StringBuilder();
while (token != MarkdownToken.NOTHING) {
if (
(token == currentToken && currentToken != MarkdownToken.WORD) ||
(token != MarkdownToken.SCREENING && isSuffix(currentToken))
) {
addToList(items, sb);
if (
!(token == currentToken &&
currentToken != MarkdownToken.WORD)
) {
currentChar++;
}
break;
}
switch (token) {
case STRIKEOUT -> {
addToList(items, sb);
Strikeout strikeout = new Strikeout(buildPart(token));
items.add(strikeout);
}
case STRONG_STAR, STRONG_UNDERLINE -> {
addToList(items, sb);
Strong strong = new Strong(buildPart(token));
items.add(strong);
}
case EMPHASIS_STAR, EMPHASIS_UNDERLINE -> {
addToList(items, sb);
Emphasis emphasis = new Emphasis(buildPart(token));
items.add(emphasis);
}
case SCREENING -> {
addToList(items, sb);
char ch = block.charAt(currentChar);
if (ch == '<') {
sb.append("&lt;");
} else if (ch == '>') {
sb.append("&gt;");
} else if (ch == '&') {
sb.append("&amp;");
} else {
sb.append(ch);
}
currentChar++;
}
case CODE -> {
addToList(items, sb);
Code code = new Code(buildPart(token));
items.add(code);
}
case VAR -> {
addToList(items, sb);
Var var = new Var(buildPart(token));
items.add(var);
}
case SAMP -> {
addToList(items, sb);
Samp samp = new Samp(buildPart(token));
items.add(samp);
}
case INS -> {
addToList(items, sb);
Ins ins = new Ins(buildPart(token));
items.add(ins);
}
case DEL -> {
addToList(items, sb);
Del del = new Del(buildPart(token));
items.add(del);
}
case PRE -> {
addToList(items, sb);
StringBuilder raw = new StringBuilder();
while (currentChar < block.length()) {
if (
isWordInBlock("```", currentChar, currentChar + 3)
) {
currentChar += 3;
break;
}
char ch = block.charAt(currentChar);
if (ch == '<') {
raw.append("&lt;");
} else if (ch == '>') {
} else if (ch == '&') {
raw.append("&amp;");
} else {
raw.append(ch);
}
currentChar++;
}
items.add(new Pre(List.of(new Text(raw.toString()))));
}
default -> {
if (currentChar < block.length()) {
if (block.charAt(currentChar) == '<') {
sb.append("&lt;");
} else if (block.charAt(currentChar) == '>') {
sb.append("&gt;");
} else if (block.charAt(currentChar) == '&') {
sb.append("&amp;");
} else {
sb.append(block.charAt(currentChar));
}
currentChar++;
}
}
}
token = nextMarkdownToken();
}
if (token == MarkdownToken.NOTHING) {
addToList(items, sb);
}
return items;
}
private MarkdownToken nextMarkdownToken() {
if (currentChar == block.length()) {
return MarkdownToken.NOTHING;
}
switch (block.charAt(currentChar)) {
case '*' -> {
if (isPrefix("**")) {
currentChar += 2;
return MarkdownToken.STRONG_STAR;
} else if (isPrefix("*")) {
currentChar += 1;
return MarkdownToken.EMPHASIS_STAR;
}
}
case '_' -> {
if (isPrefix("__")) {
currentChar += 2;
return MarkdownToken.STRONG_UNDERLINE;
} else if (isPrefix("_")) {
currentChar += 1;
return MarkdownToken.EMPHASIS_UNDERLINE;
}
}
case '-' -> {
if (isPrefix("--")) {
currentChar += 2;
return MarkdownToken.STRIKEOUT;
}
}
case '`' -> {
if (
isWordInBlock("```", currentChar, currentChar + 3) &&
hasClosing("```", currentChar + 3)
) {
currentChar += 3;
return MarkdownToken.PRE;
} else if (hasClosing("`", currentChar + 1)) {
currentChar += 1;
return MarkdownToken.CODE;
}
}
case '\\' -> {
if (isScreening(block.charAt(currentChar + 1))) {
currentChar++;
return MarkdownToken.SCREENING;
}
}
case '%' -> {
if (isPrefix("%") && hasClosing("%", currentChar + 1)) {
currentChar += 1;
return MarkdownToken.VAR;
}
}
case '!' -> {
if (isPrefix("!!") && hasClosing("!!", currentChar + 2)) {
currentChar += 2;
return MarkdownToken.SAMP;
}
}
case '<' -> {
if (
isWordInBlock("<<", currentChar, currentChar + 2) &&
hasClosing(">>", currentChar + 2)
) {
currentChar += 2;
return MarkdownToken.INS;
}
}
case '}' -> {
if (
isWordInBlock("}}", currentChar, currentChar + 2) &&
hasClosing("{{", currentChar + 2)
) {
currentChar += 2;
return MarkdownToken.DEL;
}
}
default -> {
return MarkdownToken.WORD;
}
}
return MarkdownToken.WORD;
}
private boolean hasClosing(String delimiter, int from) {
for (int j = from; j + delimiter.length() <= block.length(); j++) {
if (isWordInBlock(delimiter, j, j + delimiter.length())) {
if (j > 0 && block.charAt(j - 1) == '\\') {
continue;
}
if (delimiter.equals("`")) {
if (isWordInBlock("```", j, j + 3)) continue;
if (j >= 1 && isWordInBlock("```", j - 1, j + 2)) continue;
if (j >= 2 && isWordInBlock("```", j - 2, j + 1)) continue;
}
return true;
}
}
return false;
}
private static boolean isScreening(char ch) {
return (
ch == '*' ||
ch == '_' ||
ch == '%' ||
ch == '!' ||
ch == '<' ||
ch == '>' ||
ch == '}' ||
ch == '{' ||
ch == '`'
);
}
private boolean isSuffix(MarkdownToken token) {
if (token == MarkdownToken.WORD) {
return false;
}
String suffix = getHighlight(token);
if (isWordInBlock(suffix, currentChar, currentChar + suffix.length())) {
currentChar += suffix.length() - 1;
return true;
}
return false;
}
private boolean isPrefix(String prefix) {
int i = currentChar + prefix.length();
return (
isValidIndexInBlock(i + prefix.length()) &&
!isWordInBlock(prefix, i, i + prefix.length()) &&
isWordInBlock(prefix, i - prefix.length(), i) &&
!Character.isWhitespace(block.charAt(i))
);
}
private boolean isWordInBlock(String word, int start, int end) {
if (end > block.length() || start < 0) {
return false;
}
return block.regionMatches(start, word, 0, word.length());
}
private boolean isValidIndexInBlock(int index) {
return index < block.length();
}
private boolean isHeader() {
int levelOfHeader = 0;
if (block.charAt(levelOfHeader) != '#') {
return false;
}
while (
levelOfHeader < block.length() && block.charAt(levelOfHeader) == '#'
) {
levelOfHeader++;
}
return (
levelOfHeader < block.length() &&
Character.isWhitespace(block.charAt(levelOfHeader))
);
}
private static String getHighlight(MarkdownToken token) {
switch (token) {
case STRONG_STAR -> {
return "**";
}
case STRONG_UNDERLINE -> {
return "__";
}
case EMPHASIS_STAR -> {
return "*";
}
case EMPHASIS_UNDERLINE -> {
return "_";
}
case CODE -> {
return "`";
}
case STRIKEOUT -> {
return "--";
}
case SCREENING -> {
return "\\";
}
case VAR -> {
return "%";
}
case SAMP -> {
return "!!";
}
case INS -> {
return ">>";
}
case DEL -> {
return "{{";
}
case PRE -> {
return "```";
}
default -> {
return "";
}
}
}
private static void addToList(
List<PartOfParagraph> items,
StringBuilder sb
) {
if (!sb.isEmpty()) {
items.add(new Text(sb.toString()));
sb.setLength(0);
}
}
}