Proper handling fo multiline links. Better list handling.
[sixth.git] / src / main / java / eu / svjatoslav / sixth / core / document / Heading.java
1 package eu.svjatoslav.sixth.core.document;
2
3 import eu.svjatoslav.commons.string.String2;
4 import eu.svjatoslav.commons.string.tokenizer.Tokenizer;
5 import eu.svjatoslav.commons.string.tokenizer.TokenizerMatch;
6 import eu.svjatoslav.sixth.core.document.content.ListElement;
7 import eu.svjatoslav.sixth.core.document.text.FormattedText;
8
9 import java.util.ArrayList;
10 import java.util.List;
11
12 import static eu.svjatoslav.sixth.core.document.Helper.TG_LIST;
13
14 public class Heading {
15     public final FormattedText name;
16     public final int level;
17     public final Heading parent;
18     private final List<Heading> children = new ArrayList<>();
19
20     public final ListElement rootListElement = new ListElement(null, -2, null, "");
21     private ListElement currentListElement = rootListElement;
22
23     public Heading(FormattedText name, int level, Heading parent){
24         this.level = level;
25         this.name = name;
26         this.parent = parent;
27     }
28
29     public void addChild(Heading heading){
30         children.add(heading);
31     }
32
33     public List<Heading> getChildren(){
34         return children;
35     }
36
37     public String toMD () {
38         StringBuilder sb = new StringBuilder();
39
40         if (level > 0) sb.append(enlistTitleInMD());
41
42         rootListElement.toMD(sb, -2);
43
44         children.stream().map(Heading::toMD).forEach(sb::append);
45
46         return sb.toString();
47     }
48
49     private String enlistTitleInMD() {
50         String2 s = new String2();
51         s.append("#", level).append(" ").append(name.toMD(0)).append("\n");
52         return s.toString();
53     }
54
55     public ListElement getCurrentHeading(){
56         return currentListElement;
57     }
58
59     public void parse(TokenizerMatch tm){
60
61         if (tm.isGroup(TG_LIST)){
62             parseList(tm);
63             return;
64         }
65
66         currentListElement.parse(tm);
67     }
68
69     private void parseList(TokenizerMatch tm) {
70         String[] listSections = tm.getRegExpGroups();
71         int indent = listSections[0].length();
72         String type = listSections[1];
73         FormattedText title = FormattedText.fromOrg(parseFullListTitle(getPartialTitle(listSections), tm.getTokenizer(), indent));
74
75         if (indent > currentListElement.indent){
76             ListElement newElement = new ListElement(title, indent, currentListElement, type);
77             currentListElement.addContent(newElement);
78             currentListElement = newElement;
79             return;
80         }
81
82         if (indent == currentListElement.indent){
83             ListElement newElement = new ListElement(title, indent, currentListElement.parent, type);
84             currentListElement.parent.addContent(newElement);
85             currentListElement = newElement;
86             return;
87         }
88
89         while (true){
90             if (currentListElement.parent.indent <= indent){
91                 ListElement newElement = new ListElement(title, indent, currentListElement.parent, type);
92                 currentListElement.parent.addContent(newElement);
93                 currentListElement = newElement;
94                 return;
95             }
96             currentListElement = currentListElement.parent;
97         }
98
99     }
100
101     private String getPartialTitle(String[] listSections) {
102         return listSections.length > 2 ? listSections[2] : "";
103     }
104
105     private String parseFullListTitle(String partialTitle, Tokenizer tokenizer, int listIndent){
106         StringBuilder sb = new StringBuilder();
107         sb.append(partialTitle);
108
109         while (tokenizer.hasMoreContent()){
110             final TokenizerMatch tm = tokenizer.getNextToken();
111
112             if (isContentContinuation(tm, listIndent, Helper.TG_NORMAL_TEXT)){
113                 String titleContinuation = tm.token.substring(listIndent).trim();
114                 sb.append("\n").append(titleContinuation);
115                 continue;
116             }
117
118             tokenizer.unreadToken();
119             break;
120         }
121
122         return sb.toString();
123     }
124
125     public static boolean isContentContinuation(TokenizerMatch tm, int requiredIndent, String requiredGroup) {
126         if (tm.token.length() <= requiredIndent) return false;
127
128         return tm.isGroup(requiredGroup) && tm.token.substring(0, requiredIndent +1).trim().length() == 0;
129     }
130
131 }