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