1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.nuiton.jrst.legacy;
24
25 import static org.nuiton.jrst.legacy.ReStructuredText.BLOCK_QUOTE;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.dom4j.DocumentHelper;
30 import org.dom4j.Element;
31
32 import java.io.IOException;
33 import java.io.Reader;
34 import java.net.URLEncoder;
35 import java.util.ArrayList;
36 import java.util.LinkedList;
37 import java.util.List;
38 import java.util.regex.Matcher;
39 import java.util.regex.Pattern;
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62 public class JRSTLexer {
63
64
65 private static Log log = LogFactory.getLog(JRSTLexer.class);
66
67 public static final String BULLET_CHAR = "*" + "+" + "-"
68
69
70
71 ;
72
73 public static final String TITLE_CHAR = "-=-~'`^+:!\"#$%&*,./;|?@\\_[\\]{}<>()";
74
75 public static final String DOCINFO_ITEM = "author|authors|organization|address|contact|version|revision|status|date|copyright";
76
77 public static final String ADMONITION_PATTERN = "admonition|attention|caution|danger|error|hint|important|note|tip|warning";
78
79
80
81
82
83 public static final String TITLE = "title";
84
85
86
87
88
89 public static final String DOCINFO = "docinfo";
90
91
92
93
94
95 public static final String DECORATION = "decoration";
96
97 public static final String HEADER = "header";
98
99 public static final String FOOTER = "footer";
100
101
102
103
104
105 public static final String TRANSITION = "transition";
106
107 public static final String SIDEBAR = "sidebar";
108
109 public static final String TOPIC = "topic";
110
111
112
113
114
115 static final public String LITERAL_BLOCK = "literal_block";
116
117 static final public String PARAGRAPH = "paragraph";
118
119 static final public String BLANK_LINE = "blankLine";
120
121 static final public String COMMENT = "comment";
122
123 static final public String SUBSTITUTION_DEFINITION = "substitution_definition";
124
125 static final public String BULLET_LIST = "bullet_list";
126
127 static final public String FIELD_LIST = "field_list";
128
129 static final public String DEFINITION_LIST = "definition_list";
130
131 static final public String ENUMERATED_LIST = "enumerated_list";
132
133 static final public String OPTION_LIST = "option_list";
134
135 public static final String LINE_BLOCK = "line_block";
136
137 public static final String LINE = "line";
138
139 public static final String ATTRIBUTION = "attribution";
140
141 public static final String DOCTEST_BLOCK = "doctest_block";
142
143 public static final String ADMONITION = "admonition";
144
145 public static final String TARGET = "target";
146
147 public static final String FOOTNOTE = "footnote";
148
149 public static final String FOOTNOTES = "footnotes";
150
151 public static final String LEVEL = "level";
152
153 public static final String TARGETANONYMOUS = "targetAnonymous";
154
155
156
157
158
159
160 static final public String TABLE = "table";
161
162 static final public String ROW = "row";
163
164 static final public String CELL = "cell";
165
166 static final public String TABLE_HEADER = "header";
167
168 static final public String TABLE_WIDTH = "width";
169
170 static final public String ROW_END_HEADER = "endHeader";
171
172 static final public String CELL_INDEX_START = "indexStart";
173
174 static final public String CELL_INDEX_END = "indexEnd";
175
176 static final public String CELL_BEGIN = "begin";
177
178 static final public String CELL_END = "end";
179
180 static final public String REMOVE = "remove";
181
182 static final public String INCLUDE = "include";
183
184
185
186
187
188 static final public String DIRECTIVE = "directive";
189
190 static final public String DIRECTIVE_TYPE = "type";
191
192 static final public String DIRECTIVE_VALUE = "value";
193
194
195
196
197
198
199 static final public String AUTONUM = "autoNum";
200
201 static final public String AUTONUMLABEL = "autoNumLabel";
202
203 static final public String AUTOSYMBOL = "autoSymbol";
204
205 static final public String BULLET = "bullet";
206
207 static final public String CHAR = "char";
208
209 static final public String ID = "id";
210
211 static final public String CLASSIFIERS = "classifiers";
212
213 static final public String DELIMITER = "delimiter";
214
215 static final public String DELIMITEREXISTE ="delimiterExiste";
216
217 static final public String ENUMTYPE = "enumtype";
218
219 static final public String REFURI = "refuri";
220
221 static final public String OPTION = "option";
222
223 static final public String LITERAL = "literal";
224
225 static final public String NAME = "name";
226
227 static final public String NUM ="num";
228
229 static final public String OPTIONARGUMENT = "option_argument";
230
231 static final public String OPTIONSTRING = "option_string";
232
233 static final public String PREFIX = "prefix";
234
235 static final public String START = "start";
236
237 static final public String SUBEXISTE = "subExiste";
238
239 static final public String SUFFIX = "suffix";
240
241 static final public String SUBTITLE = "subtitle";
242
243 static final public String TERM = "term";
244
245 static final public String TITLEATTR = "title";
246
247 static final public String XMLSPACE = "xml:space";
248
249 static final public String TYPE = "type";
250
251
252
253
254 protected static final String TRUE = "true";
255
256 protected static final String FALSE = "false";
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273 private List<String> titleLevels;
274
275 private AdvancedReader in;
276
277
278
279
280 private int elementLength;
281
282
283
284
285 public JRSTLexer(Reader reader) {
286 titleLevels = new ArrayList<String>();
287 in = new AdvancedReader(reader);
288 }
289
290
291
292
293
294
295
296 public boolean eof() throws IOException {
297 in.mark();
298 in.skipBlankLines();
299 boolean result = in.eof();
300 in.reset();
301 return result;
302 }
303
304
305
306
307
308
309 public void remove() throws IOException {
310 in.skip(elementLength);
311 }
312
313
314
315
316
317
318 private void beginPeek() throws IOException {
319 elementLength = 0;
320 in.mark();
321 }
322
323
324
325
326
327
328 private void endPeek() throws IOException {
329 elementLength = in.readSinceMark();
330 in.reset();
331 }
332
333
334
335
336
337
338
339
340
341
342 private String[] readBlock(int minLeftMargin) throws IOException {
343 String[] result = new String[0];
344 String firstLine = in.readLine();
345 if (firstLine != null) {
346 in.unread(firstLine, true);
347 int level = level(firstLine);
348 if (level >= minLeftMargin) {
349 result = in.readWhile("^\\s{" + level + "}\\S+.*");
350 }
351 }
352
353 return result;
354 }
355
356
357
358
359
360
361
362
363 private String joinBlock(String[] lines) {
364 String result = joinBlock(lines, " ", true);
365 return result;
366 }
367
368
369
370
371
372
373
374
375
376
377
378
379
380 private String joinBlock(String[] lines, String joinSep, boolean trim) {
381 String result = "";
382 String sep = "";
383 for (String line : lines) {
384 if (trim) {
385 line = line.trim();
386 }
387 result += sep + line;
388 sep = joinSep;
389 }
390 return result;
391 }
392
393
394
395
396
397
398
399
400
401
402
403 public Element peekHeader() throws IOException {
404 beginPeek();
405 Element result = null;
406 String[] line = in.readAll();
407 if (line != null) {
408 int i = 0;
409 for (String l : line) {
410 i++;
411 if (l.matches("^\\s*.. " + HEADER + ":: .*")) {
412 int level = level(l);
413 l = l.replaceAll("^\\s*.. " + HEADER + ":: ", "");
414 result = DocumentHelper.createElement(HEADER).addAttribute(
415 LEVEL, String.valueOf(level));
416 result.addAttribute(LINE, "" + i);
417 result.setText(l);
418 }
419
420 }
421 }
422 endPeek();
423 return result;
424
425 }
426
427
428
429
430
431
432
433
434
435
436
437 public Element peekFooter() throws IOException {
438 beginPeek();
439 Element result = null;
440 String[] line = in.readAll();
441 if (line != null) {
442 int i = 0;
443 for (String l : line) {
444 i++;
445
446 if (l.matches("^\\s*.. " + FOOTER + ":: .*")) {
447 int level = level(l);
448 l = l.replaceAll("^\\s*.. " + FOOTER + ":: ", "");
449 result = DocumentHelper.createElement(FOOTER).addAttribute(
450 LEVEL, String.valueOf(level));
451 result.addAttribute(LINE, "" + i);
452 result.setText(l);
453 }
454 }
455 }
456 endPeek();
457 return result;
458
459 }
460
461
462
463
464
465
466
467
468
469 public LinkedList<Element> peekTargetAnonymous() throws IOException {
470 beginPeek();
471 LinkedList<Element> result = new LinkedList<Element>();
472 String[] line = in.readAll();
473 if (line != null) {
474 int i = 0;
475 for (String l : line) {
476 i++;
477
478 if (l.matches("^\\s*__ .+$|^\\s*\\.\\. __\\:.+$")) {
479 log.debug(l);
480 Element resultTmp = DocumentHelper
481 .createElement(TARGETANONYMOUS);
482 resultTmp.addAttribute(LEVEL, "" + level(l));
483 Matcher matcher = Pattern.compile("__ |.. __: ").matcher(l);
484
485 if (matcher.find()) {
486 resultTmp.addAttribute(REFURI, l.substring(matcher
487 .end(), l.length()));
488 }
489
490 result.add(resultTmp);
491 }
492 }
493
494 }
495 endPeek();
496 return result;
497 }
498
499
500
501
502
503
504
505 public Element peekTitleOrBodyElement() throws IOException {
506 Element result = null;
507 if (result == null) {
508 result = peekTitle();
509 }
510 if (result == null) {
511 result = peekBodyElement();
512 }
513
514 return result;
515 }
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530 public Element peekDocInfo() throws IOException {
531 Element result = null;
532 if (result == null) {
533 result = peekDocInfoItem();
534
535 }
536 if (result == null) {
537 result = peekFieldList();
538 }
539
540 return result;
541
542 }
543
544
545
546
547
548
549
550 public Element peekBodyElement() throws IOException {
551 Element result = null;
552 if (result == null) {
553 result = peekInclude();
554 }
555 if (result == null) {
556 result = peekDoctestBlock();
557 }
558 if (result == null) {
559 result = peekAdmonition();
560 }
561 if (result == null) {
562 result = peekSidebar();
563 }
564 if (result == null) {
565 result = peekTopic();
566 }
567 if (result == null) {
568 result = peekRemove();
569 }
570 if (result == null) {
571 result = peekDirectiveOrReference();
572 }
573 if (result == null) {
574 result = peekTransition();
575 }
576 if (result == null) {
577 result = peekTable();
578 }
579 if (result == null) {
580 result = peekLineBlock();
581 }
582 if (result == null) {
583 result = peekBulletList();
584 }
585 if (result == null) {
586 result = peekOption();
587 }
588 if (result == null) {
589 result = peekEnumeratedList();
590 }
591 if (result == null) {
592 result = peekTarget();
593 }
594 if (result == null) {
595 result = peekFootnote();
596 }
597
598
599 if (result == null) {
600 result = peekComment();
601 }
602 if (result == null) {
603 result = peekDefinitionList();
604 }
605 if (result == null) {
606 result = peekFieldList();
607 }
608 if (result == null) {
609 result = peekTargetAnonymousBody();
610 }
611 if (result == null) {
612 result = peekLiteralBlock();
613 }
614 if (result == null) {
615 result = peekBlockQuote();
616 }
617 if (result == null) {
618 result = peekBlankLine();
619 }
620 if (result == null) {
621 result = peekPara();
622 }
623
624 return result;
625 }
626
627
628
629
630
631
632
633 public Element peekRemove() throws IOException {
634 beginPeek();
635 Element result = null;
636 String line = in.readLine();
637 if (line != null) {
638
639 if (line.matches("^\\s*.. " + HEADER + ":: .*")) {
640 result = DocumentHelper.createElement(REMOVE).addAttribute(
641 LEVEL, "" + level(line));
642 }
643
644 if (line.matches("^\\s*.. " + FOOTER + ":: .*")) {
645 result = DocumentHelper.createElement(REMOVE).addAttribute(
646 LEVEL, "" + level(line));
647 }
648
649 }
650 endPeek();
651 return result;
652 }
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668 private Element peekInclude() throws IOException {
669 beginPeek();
670 Element result = null;
671 String line = in.readLine();
672 if (line != null) {
673 if (line.matches("^\\s*\\.\\.\\sinclude\\:\\:.+$")) {
674 result = DocumentHelper.createElement(INCLUDE);
675 result.addAttribute(LEVEL, "" + level(line));
676 String option = line.substring(line.indexOf("::") + 2).trim();
677 result.addAttribute(OPTION, "");
678 if (option.trim().equalsIgnoreCase(LITERAL)) {
679 result.addAttribute(OPTION, LITERAL);
680 line = in.readLine();
681 result.setText(line.trim());
682 } else {
683 result.setText(option);
684 }
685
686 }
687 }
688 endPeek();
689 return result;
690 }
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716 public Element peekOption() throws IOException {
717
718 beginPeek();
719 Element result = null;
720 String line = in.readLine();
721 if (line != null) {
722 if (line.matches("^(\\s*((--?)|(//?))\\w+([ =][<a-zA-Z][\\w-><]*)?)\\s*.*$")) {
723 result = DocumentHelper.createElement(OPTION_LIST)
724 .addAttribute(LEVEL, "" + level(line));
725 char delimiter;
726 do {
727 Matcher matcher = Pattern.compile("[-/][-/]?.+").matcher(line);
728 matcher.find();
729 Element option = result.addElement(OPTION);
730 String option_stringTmp = matcher.group();
731 matcher = Pattern.compile("^[-/][-/]?\\w+").matcher(option_stringTmp);
732 matcher.find();
733 String option_string = matcher.group();
734 option.addAttribute(OPTIONSTRING, option_string);
735
736 boolean done = false;
737
738 delimiter = '.';
739 if (option_stringTmp.length() > matcher.end()) {
740 delimiter = option_stringTmp.charAt(matcher.end());
741 option_stringTmp = option_stringTmp.substring(
742 matcher.end(), option_stringTmp.length());
743 } else {
744 done = true;
745 }
746 option.addAttribute(DELIMITEREXISTE, FALSE);
747
748 if (delimiter == ' ') {
749
750 if (option_stringTmp.charAt(1) == ' ') {
751 done = true;
752 }
753 }
754 String option_argument = null;
755 if ((delimiter == '=' || delimiter == ' ') && !done) {
756 option.addAttribute(DELIMITEREXISTE, TRUE);
757 option.addAttribute(DELIMITER, "" + delimiter);
758 matcher = Pattern.compile(delimiter + "(([a-zA-Z][\\w-]+)|(<[a-zA-Z][^>]*>))").matcher(
759 option_stringTmp);
760 if (matcher.find()) {
761 option_argument = matcher.group().substring(1,
762 matcher.group().length());
763 option.addAttribute(OPTIONARGUMENT, option_argument);
764 int size = option_argument.length() + 1;
765 if (option_stringTmp.length() < size && option_stringTmp.charAt(size) == ',') {
766 delimiter = ',';
767 } else {
768 done = true;
769 }
770 } else {
771
772 option_argument = option_stringTmp;
773 option.addAttribute(OPTIONARGUMENT,
774 option_argument);
775 line = in.readLine();
776 if (line != null) {
777 result.setText(line.trim());
778 }
779 }
780 }
781 if (delimiter == ',') {
782 line = line.substring(option_string.length() + 1
783 + (option_argument == null ? 0 : option_argument.length()));
784 }
785 if (done) {
786 result.setText(option_stringTmp.substring(matcher.end(), option_stringTmp.length()).trim()
787 + " " + joinBlock(readBlock(1)));
788 }
789 } while (delimiter == ',');
790 }
791 }
792 endPeek();
793 return result;
794 }
795
796
797
798
799
800
801
802
803
804
805
806
807 private Element peekTopic() throws IOException {
808 beginPeek();
809 Element result = null;
810 String line = in.readLine();
811 if (line != null) {
812 if (line.matches("^\\.\\.\\s+(" + TOPIC + ")::\\s+(.*)$")) {
813 Matcher matcher = Pattern.compile(TOPIC + "::").matcher(line);
814 matcher.find();
815 result = DocumentHelper.createElement(TOPIC).addAttribute(
816 LEVEL, "" + level(line));
817 String title = line.substring(matcher.end(), line.length());
818 result.addAttribute(TITLE, title);
819 line = in.readLine();
820 if (line.matches("\\s*")) {
821 line = in.readLine();
822 }
823 int level = level(line);
824 String[] lines = null;
825 if (level != 0) {
826 lines = in.readWhile("(^ {" + level + "}.*)|(\\s*)");
827 String txt = line;
828 for (String txtTmp : lines) {
829 txt += "\n" + txtTmp.trim();
830 }
831 result.setText(txt);
832 }
833
834 }
835 }
836
837 endPeek();
838 return result;
839 }
840
841
842
843
844
845
846
847
848
849
850
851
852
853 private Element peekSidebar() throws IOException {
854 beginPeek();
855 Element result = null;
856 String line = in.readLine();
857 if (line != null) {
858 if (line.matches("^\\.\\.\\s*(" + SIDEBAR + ")::\\s*(.*)$")) {
859 Matcher matcher = Pattern.compile(SIDEBAR + "::").matcher(line);
860 matcher.find();
861 result = DocumentHelper.createElement(SIDEBAR).addAttribute(
862 LEVEL, "" + level(line));
863 String title = line.substring(matcher.end(), line.length());
864 result.addAttribute(TITLE, title);
865 line = in.readLine();
866 if (line.matches("^\\s+:subtitle:\\s*(.*)$*")) {
867 matcher = Pattern.compile(":subtitle:\\s*").matcher(line);
868 matcher.find();
869 String subTitle = line.substring(matcher.end(), line
870 .length());
871 result.addAttribute(SUBEXISTE, TRUE);
872 result.addAttribute(SUBTITLE, subTitle);
873 line = in.readLine();
874 } else {
875 result.addAttribute(SUBEXISTE, FALSE);
876 }
877 String txt = joinBlock(readBlock(level(line)));
878 result.setText(txt);
879
880 }
881 }
882
883 endPeek();
884 return result;
885 }
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902 private Element peekLineBlock() throws IOException {
903 beginPeek();
904 Element result = null;
905 String line = in.readLine();
906 if (line != null) {
907 if (line.matches("\\|\\s.*")) {
908 String[] linesTmp = readBlock(0);
909 String[] lines = new String[linesTmp.length + 1];
910 lines[0] = line;
911 for (int i = 0; i < linesTmp.length; i++) {
912 lines[i + 1] = linesTmp[i];
913 }
914 int[] levelsTmp = new int[lines.length];
915 int levelmin = 999;
916 result = DocumentHelper.createElement(LINE_BLOCK).addAttribute(
917 LEVEL, 0 + "");
918 for (int i = 0; i < levelsTmp.length; i++) {
919
920 lines[i] = lines[i].replaceAll("\\|\\s?", "");
921 }
922 for (int i = 0; i < levelsTmp.length; i++) {
923
924 levelsTmp[i] = level(lines[i]);
925 }
926 for (int i : levelsTmp) {
927
928 levelmin = Math.min(levelmin, i);
929 }
930 int cnt = 0;
931 String lineAv = "";
932 int[] levels = new int[levelsTmp.length];
933 for (String l : lines) {
934 if (!l.matches("\\s*")) {
935
936 int level = levelsTmp[cnt] - levelmin;
937 if (level != 0) {
938 if (cnt != 0) {
939 if (!lineAv.matches("\\s*")) {
940
941
942 int levelAv = levelsTmp[cnt - 1] - levelmin;
943 if (levelAv < level) {
944 levels[cnt] = levels[cnt - 1] + 1;
945 if (cnt != levels.length) {
946 int levelAp = levelsTmp[cnt + 1]
947 - levelmin;
948 if (levelAp < level
949 && levelAv < levelAp) {
950 levels[cnt]++;
951 }
952 }
953 } else {
954 levels[cnt] = levels[cnt - 1] - 1;
955 }
956 } else {
957 levels[cnt] = 1;
958 }
959 } else {
960 levels[cnt] = 1;
961 }
962 } else {
963 levels[cnt] = 0;
964 }
965 } else {
966 if (cnt != 0) {
967 levels[cnt] = levels[cnt - 1];
968 }
969 else {
970 levels[cnt] = 0;
971 }
972 }
973 cnt++;
974 lineAv = l;
975 }
976 for (int i = 0; i < levels.length; i++) {
977 Element eLine = result.addElement(LINE);
978 eLine.addAttribute(LEVEL, "" + levels[i]);
979 eLine.setText(lines[i].trim());
980 }
981 }
982 }
983 endPeek();
984 return result;
985 }
986
987
988
989
990
991
992
993
994
995
996
997
998 private Element peekDoctestBlock() throws IOException {
999 beginPeek();
1000 Element result = null;
1001 String line = in.readLine();
1002 if (line != null) {
1003 if (line.matches("^\\s*>>>\\s.*")) {
1004 int level = level(line);
1005 result = DocumentHelper.createElement(DOCTEST_BLOCK)
1006 .addAttribute(LEVEL, String.valueOf(level));
1007 result.addAttribute(XMLSPACE, "preserve");
1008 line += "\n" + joinBlock(readBlock(level));
1009 result.setText(line);
1010 }
1011 }
1012 endPeek();
1013 return result;
1014 }
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030 private Element peekBlockQuote() throws IOException {
1031 beginPeek();
1032 Element result = null;
1033 String line = in.readLine();
1034 if (line != null) {
1035 if (line.matches("\\s.*")) {
1036 int level = level(line);
1037 String savedLine = line + " " + joinBlock(readBlock(level));
1038 line = in.readLine();
1039
1040 if (line != null) {
1041 level = level(line);
1042 String blockQuote = null;
1043 if (level != 0) {
1044 String txt = line;
1045 String[] lines = in.readWhile("(^ {" + level
1046 + "}.*)|(\\s*)");
1047 for (String l : lines) {
1048 if (l.matches("^ {" + level + "}--\\s*.*")) {
1049 blockQuote = l;
1050 blockQuote = blockQuote.replaceAll("--", "")
1051 .trim();
1052 } else {
1053 txt += "\n" + l;
1054 }
1055 }
1056 result = DocumentHelper.createElement(BLOCK_QUOTE)
1057 .addAttribute(LEVEL, String.valueOf(level));
1058 if (blockQuote != null) {
1059 result.addAttribute(ATTRIBUTION, blockQuote);
1060 }
1061 result.setText(savedLine + txt);
1062 }
1063 }
1064 }
1065 }
1066 endPeek();
1067 return result;
1068 }
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084 protected Element peekAdmonition() throws IOException {
1085 beginPeek();
1086 Element result = null;
1087 String line = in.readLine();
1088 if (line != null) {
1089 String lineTest = line.toLowerCase();
1090 Pattern pAdmonition = Pattern.compile("^\\s*\\.\\.\\s("
1091 + ADMONITION_PATTERN + ")::\\s*(.*)$");
1092 Matcher matcher = pAdmonition.matcher(lineTest);
1093
1094 if (matcher.matches()) {
1095
1096 boolean admonition = false;
1097 matcher = Pattern.compile(ADMONITION_PATTERN).matcher(lineTest);
1098 matcher.find();
1099 int level = level(line);
1100 result = DocumentHelper.createElement(ADMONITION).addAttribute(
1101 LEVEL, "" + level);
1102
1103 if (matcher.group().equals(ADMONITION)) {
1104
1105
1106
1107 admonition = true;
1108 result.addAttribute(TYPE, ADMONITION);
1109 String title = line.substring(matcher.end() + 2, line
1110 .length());
1111
1112 result.addAttribute(TITLEATTR, title);
1113 } else {
1114 result.addAttribute(TYPE, matcher.group());
1115 }
1116
1117 String firstLine = "";
1118 if (!admonition && matcher.end() + 2 < line.length()) {
1119 firstLine = line
1120 .substring(matcher.end() + 2, line.length());
1121 }
1122 line = in.readLine();
1123 if (line != null) {
1124 if (line.matches("\\s*")) {
1125 line = in.readLine();
1126 }
1127 if (line != null && !line.matches("\\s*")) {
1128 level = level(line);
1129 String txt = firstLine.trim() + "\n" + line + "\n";
1130 txt += "\n" + readBlockWithBlankLine(level);
1131 result.setText(txt);
1132 } else {
1133 result.setText(firstLine);
1134 }
1135 } else {
1136 result.setText(firstLine);
1137 }
1138 }
1139 }
1140 endPeek();
1141 return result;
1142 }
1143
1144
1145
1146
1147
1148
1149
1150 public Element peekBlankLine() throws IOException {
1151 beginPeek();
1152 Element result = null;
1153
1154
1155 String line = in.readLine();
1156 if (line != null && line.matches("\\s*")) {
1157 int level = level(line);
1158 result = DocumentHelper.createElement(BLANK_LINE).addAttribute(
1159 LEVEL, String.valueOf(level));
1160 }
1161
1162 endPeek();
1163 return result;
1164 }
1165
1166
1167
1168
1169
1170
1171
1172 public Element peekDirectiveOrReference() throws IOException {
1173 beginPeek();
1174 Element result = null;
1175 String line = in.readLine();
1176 if (line != null) {
1177 Pattern pImage = Pattern
1178 .compile("^\\.\\.\\s*(?:\\|([^|]+)\\|)?\\s*(\\w+)::\\s*(.*)$");
1179 Matcher matcher = pImage.matcher(line);
1180 if (matcher.matches()) {
1181 String ref = matcher.group(1);
1182 String directiveType = matcher.group(2);
1183 String directiveValue = matcher.group(3);
1184 Element directive = null;
1185 if (ref != null && !"".equals(ref)) {
1186 result = DocumentHelper
1187 .createElement(SUBSTITUTION_DEFINITION);
1188 result.addAttribute(NAME, ref);
1189 directive = result.addElement(DIRECTIVE);
1190 } else {
1191 result = DocumentHelper.createElement(DIRECTIVE);
1192 directive = result;
1193 }
1194 result.addAttribute(LEVEL, "0");
1195
1196 directive.addAttribute(DIRECTIVE_TYPE, directiveType);
1197 directive.addAttribute(DIRECTIVE_VALUE, directiveValue);
1198
1199 String[] lines = readBlock(1);
1200 String text = joinBlock(lines, "\n", false);
1201
1202 directive.setText(text);
1203 }
1204 }
1205 endPeek();
1206 return result;
1207 }
1208
1209
1210
1211
1212
1213
1214
1215 public Element peekTransition() throws IOException {
1216 beginPeek();
1217
1218 Element result = null;
1219
1220
1221
1222 String line = in.readLine();
1223 if (line != null && line.matches("\\s*")) {
1224
1225 line = in.readLine();
1226 if (line != null && line.matches("-{3,}\\s*")) {
1227 line = in.readLine();
1228
1229 if (line != null && line.matches("\\s*")) {
1230 result = DocumentHelper.createElement(TRANSITION)
1231 .addAttribute(LEVEL, String.valueOf(0));
1232 }
1233 }
1234 }
1235 endPeek();
1236 return result;
1237 }
1238
1239
1240
1241
1242
1243
1244
1245
1246 public Element peekPara() throws IOException {
1247 beginPeek();
1248
1249 Element result = null;
1250
1251
1252 String[] lines;
1253 do {
1254 lines = readBlock(0);
1255 if (lines.length > 0) {
1256 int level = level(lines[0]);
1257 String para = joinBlock(lines);
1258
1259 boolean literal = false;
1260 if (para.endsWith(": ::")) {
1261 para = para.substring(0, para.length() - " ::".length());
1262
1263 in.unread("::", true);
1264 for(int i=0;i<level;i++) {
1265 in.add(' ');
1266 }
1267
1268 literal = true;
1269 } else if (para.endsWith("::")) {
1270 para = para.substring(0, para.length() - ":".length());
1271
1272
1273
1274 in.unread("::", true);
1275
1276 for(int i=0;i<level;i++) {
1277 in.add(' ');
1278 }
1279
1280 literal = true;
1281 }
1282
1283 if (para.length() == 0 || ":".equals(para)) {
1284 if (literal) {
1285 in.readLine();
1286 }
1287 } else {
1288
1289
1290 result = DocumentHelper.createElement(PARAGRAPH)
1291 .addAttribute(LEVEL, String.valueOf(level))
1292 .addText(para);
1293 }
1294 }
1295 } while (result == null && lines.length > 0);
1296
1297 endPeek();
1298 return result;
1299 }
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313 public Element peekLiteralBlock() throws IOException {
1314 beginPeek();
1315
1316 Element result = null;
1317
1318
1319
1320 String[] prefix = in.readLines(2);
1321 if (prefix.length == 2 && prefix[0].matches("\\s*::\\s*")
1322 && prefix[1].matches("\\s*")) {
1323
1324 int level = level(prefix[0]);
1325
1326 String para = in.readLine();
1327 if (para != null) {
1328 level=level+1;
1329 para = para.substring(level) + "\n";
1330
1331
1332 String[] lines = in.readWhile("(^ {" + level + "}.*|\\s*)");
1333 while (lines.length > 0) {
1334 for (String line : lines) {
1335 if (!line.matches("\\s*")) {
1336 para += line.substring(level) + "\n";
1337 }
1338 else {
1339 para += "\n";
1340 }
1341 }
1342 lines = in.readWhile("(^ {" + level + "}.*|\\s*)");
1343 }
1344
1345 result = DocumentHelper.createElement(LITERAL_BLOCK)
1346 .addAttribute(LEVEL, String.valueOf(level)).addText(
1347 para);
1348 }
1349 }
1350
1351 endPeek();
1352 return result;
1353 }
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368 public Element peekDocInfoItem() throws IOException {
1369 beginPeek();
1370
1371 Element result = null;
1372
1373 String line = in.readLine();
1374
1375 if (line != null && line.matches("^:((?i)" + DOCINFO_ITEM + "):.*$")) {
1376
1377 result = DocumentHelper.createElement(DOCINFO);
1378 result.addAttribute(LEVEL, "0");
1379 String infotype = line.substring(1, line.indexOf(":", 1));
1380
1381
1382
1383
1384
1385 String text = line.substring(line.indexOf(":", 1) + 1).trim();
1386 String[] textTmp = in.readWhile("^\\s+.*");
1387 if (textTmp.length != 0) {
1388 in.mark();
1389 }
1390
1391 for (String txt : textTmp) {
1392 text += "\n" + txt.trim();
1393 }
1394
1395
1396 text = text.replaceAll("\\$\\w+: (.+?)\\$", "$1");
1397
1398 result.addAttribute(TYPE, infotype).addText(text);
1399 }
1400 endPeek();
1401 return result;
1402 }
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421 @SuppressWarnings("unchecked")
1422 public Element peekTable() throws IOException {
1423 beginPeek();
1424
1425 Element result = null;
1426
1427 String line = in.readLine();
1428
1429 if (line != null) {
1430 Pattern pTableBegin = Pattern.compile("^\\s*(\\+-+)+\\+\\s*$");
1431 Matcher matcher = null;
1432
1433 matcher = pTableBegin.matcher(line);
1434 if (matcher.matches()) {
1435 result = DocumentHelper.createElement(TABLE);
1436 result.addAttribute(TABLE_HEADER, FALSE);
1437 int level = level(line);
1438 result.addAttribute(LEVEL, String.valueOf(level));
1439 line = line.trim();
1440 int tableWidth = line.length();
1441 result.addAttribute(TABLE_WIDTH, String.valueOf(tableWidth));
1442
1443 Pattern pCellEnd = Pattern
1444 .compile("^\\s{"
1445 + level
1446 + "}(\\+-+\\+|\\|(?:[^+]+))([^+]+(?:\\+|\\|\\s*$)|-+\\+)*\\s*");
1447
1448
1449 Pattern pCell = Pattern.compile("^\\s{" + level
1450 + "}(\\|[^|]+)+\\|\\s*$");
1451 Pattern pHeader = Pattern.compile("^\\s{" + level
1452 + "}(\\+=+)+\\+\\s*$");
1453 Pattern pEnd = Pattern.compile("^\\s{" + level
1454 + "}(\\+-+)+\\+\\s*$");
1455
1456
1457 String lastSeparationLine = line;
1458 String lastLine = line;
1459
1460 Element row = DocumentHelper.createElement(ROW);
1461 String[] table = in.readUntilBlank();
1462
1463 boolean done = false;
1464 for (String l : table) {
1465 done = false;
1466 l = l.trim();
1467 if (l.length() != tableWidth) {
1468
1469 result = null;
1470 break;
1471 }
1472 matcher = pEnd.matcher(l);
1473 if (!done && matcher.matches()) {
1474
1475 lastSeparationLine = l;
1476 for (Element cell : (List<Element>) row.elements()) {
1477 cell.addAttribute(CELL_END, TRUE);
1478 }
1479 row.addAttribute(ROW_END_HEADER, FALSE);
1480 result.add(row);
1481 row = DocumentHelper.createElement(ROW);
1482 done = true;
1483 }
1484 matcher = pHeader.matcher(l);
1485 if (!done && matcher.matches()) {
1486
1487 lastSeparationLine = l;
1488 for (Element cell : (List<Element>) row.elements()) {
1489 cell.addAttribute(CELL_END, TRUE);
1490 }
1491 row.addAttribute(ROW_END_HEADER, TRUE);
1492 result.add(row);
1493 result.addAttribute(TABLE_HEADER, TRUE);
1494 row = DocumentHelper.createElement(ROW);
1495 done = true;
1496 }
1497 matcher = pCell.matcher(l);
1498 if (!done && matcher.matches()) {
1499
1500 row.addAttribute("debug", "pCell");
1501
1502 int start = -1;
1503 String content = "";
1504 matcher = Pattern.compile("([^|]+)\\|").matcher(l);
1505 for (int cellNumber = 0; matcher.find(); cellNumber++) {
1506 int tmpstart = matcher.start(1);
1507 int end = matcher.end(1);
1508 String tmpcontent = matcher.group(1);
1509
1510
1511
1512
1513 if ((lastLine.charAt(end) == '|' || lastLine
1514 .charAt(end) == '+')
1515 && lastSeparationLine.charAt(end) == '+') {
1516 if ("".equals(content)) {
1517 content = tmpcontent;
1518 }
1519 else {
1520 content += tmpcontent;
1521 }
1522 if (start == -1) {
1523 start = tmpstart;
1524 }
1525 Element cell = null;
1526 if (row.nodeCount() <= cellNumber) {
1527 cell = row.addElement(CELL);
1528 cell.addAttribute(CELL_END, FALSE);
1529 } else {
1530 cell = (Element) row.node(cellNumber);
1531 }
1532 cell.addAttribute(CELL_INDEX_START, String
1533 .valueOf(start));
1534 cell.addAttribute(CELL_INDEX_END, String
1535 .valueOf(end));
1536 cell.setText(cell.getText() + content + "\n");
1537 start = end + 1;
1538
1539 content = "";
1540 } else {
1541
1542 if (start == -1) {
1543 start = tmpstart;
1544 }
1545 content += tmpcontent + "|";
1546 cellNumber--;
1547 }
1548 }
1549 done = true;
1550 }
1551 matcher = pCellEnd.matcher(l);
1552 if (!done && matcher.matches()) {
1553
1554 row.addAttribute("debug", "pCellEnd");
1555
1556
1557
1558
1559
1560
1561
1562 for (Element cell : (List<Element>) row.elements()) {
1563 cell.addAttribute(CELL_END, TRUE);
1564 }
1565
1566 StringBuffer tmp = new StringBuffer(l);
1567 int start = -1;
1568 String content = "";
1569 matcher = Pattern.compile("([^+|]+|-+)([+|])").matcher(
1570 l);
1571 for (int cellNumber = 0; matcher.find(); cellNumber++) {
1572 int tmpstart = matcher.start(1);
1573 int end = matcher.end(1);
1574 String tmpcontent = matcher.group(1);
1575 String ender = matcher.group(2);
1576 if (!tmpcontent.matches("-+")) {
1577
1578
1579
1580 if (lastLine.charAt(end) == '|') {
1581 if (start == -1) {
1582 start = tmpstart;
1583 }
1584
1585
1586 String old = lastSeparationLine.substring(
1587 start - 1, end + 1);
1588 tmp.replace(start - 1, end + 1, old);
1589 if ("".equals(content)) {
1590 content = tmpcontent;
1591 }
1592 Element cell = null;
1593 if (row.nodeCount() <= cellNumber) {
1594 cell = row.addElement(CELL);
1595 } else {
1596 cell = (Element) row.node(cellNumber);
1597
1598 }
1599 cell.setText(cell.getText() + content
1600 + "\n");
1601
1602
1603
1604 cell.addAttribute(CELL_END, FALSE);
1605 cell.addAttribute(CELL_INDEX_START, String
1606 .valueOf(start));
1607 cell.addAttribute(CELL_INDEX_END, String
1608 .valueOf(end));
1609 start = end + 1;
1610
1611 content = "";
1612 } else {
1613
1614 content += tmpcontent + ender;
1615 }
1616 }
1617 }
1618 lastSeparationLine = tmp.toString();
1619 row.addAttribute(ROW_END_HEADER, FALSE);
1620 result.add(row);
1621 row = DocumentHelper.createElement(ROW);
1622 done = true;
1623 }
1624 if (!done) {
1625 log.warn("Bad table format line " + in.getLineNumber());
1626 }
1627 lastLine = l;
1628 }
1629
1630
1631
1632
1633
1634 } else if (line.matches("^\\s*(=+ +)+=+\\s*$")) {
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647 result = DocumentHelper.createElement(TABLE);
1648 line = line.trim();
1649 Pattern pBordersEquals = Pattern.compile("^\\s*(=+ +)+=+\\s*$");
1650
1651 Pattern pBordersTiret = Pattern.compile("^\\s*(-+ +)+-+\\s*$");
1652
1653 Pattern pBorders = Pattern.compile("^\\s*([=-]+ +)+[=-]+\\s*$");
1654
1655
1656 String[] table = in.readUntilBlank();
1657
1658
1659 int tableWidth = line.length();
1660 int nbSeparations = 0;
1661 for (String l : table) {
1662 if (l.length() > tableWidth) {
1663 tableWidth = l.length();
1664 }
1665
1666 matcher = pBordersEquals.matcher(l);
1667 if (matcher.matches()) {
1668 nbSeparations++;
1669 }
1670
1671 }
1672
1673 result.addAttribute(TABLE_HEADER, "" + (nbSeparations == 2));
1674 int level = level(line);
1675 result.addAttribute(LEVEL, String.valueOf(level));
1676 result
1677 .addAttribute(TABLE_WIDTH, String
1678 .valueOf(tableWidth + 1));
1679 Element row = DocumentHelper.createElement(ROW);
1680
1681 List<Integer> columns = new LinkedList<Integer>();
1682 matcher = Pattern.compile("=+\\s+").matcher(line);
1683 for (int cellNumber = 0; matcher.find(); cellNumber++) {
1684 columns.add(matcher.end());
1685 }
1686 columns.add(tableWidth);
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704 String lineRef = line.replace('=', '-');
1705 Matcher matcher2;
1706 List<String> tableTmp = new LinkedList<String>();
1707
1708 for (int i = 0; i < table.length - 1; i++) {
1709 tableTmp.add(table[i]);
1710 if (!table[i].equals("")) {
1711 if (!table[i + 1]
1712 .substring(0, columns.get(0))
1713 .matches("\\s*")) {
1714 matcher = pBorders.matcher(table[i]);
1715 matcher2 = pBorders.matcher(table[i + 1]);
1716 if (!matcher.matches() && !matcher2.matches()
1717 && !table[i + 1].equals("")) {
1718 tableTmp.add(lineRef);
1719 }
1720 }
1721 }
1722 }
1723 tableTmp.add(table[table.length - 1]);
1724 table = new String[tableTmp.size()];
1725 for (int i = 0; i < tableTmp.size(); i++) {
1726 table[i] = tableTmp.get(i);
1727 }
1728
1729 boolean done = false;
1730 LinkedList<String> lastLines = new LinkedList<String>();
1731 int separation = 1;
1732 for (String l : table) {
1733 if (l != null) {
1734 done = false;
1735 matcher = pBordersTiret.matcher(l);
1736 matcher2 = pBordersEquals.matcher(l);
1737 if (matcher.matches() || matcher2.matches()) {
1738
1739 while (!lastLines.isEmpty()) {
1740 matcher = Pattern.compile("[-=]+\\s*").matcher(
1741 l);
1742 String tmpLine = lastLines.getLast();
1743 lastLines.removeLast();
1744 int cellNumber;
1745 for (cellNumber = 0; matcher.find(); cellNumber++) {
1746 Element cell = null;
1747 if (row.nodeCount() <= cellNumber) {
1748 cell = row.addElement(CELL);
1749 } else {
1750 cell = (Element) row.node(cellNumber);
1751 }
1752 if (matcher.start() < tmpLine.length()) {
1753 if (columns.size() - 1 == cellNumber) {
1754 cell.setText(tmpLine.substring(
1755 matcher.start(), tmpLine
1756 .length())
1757 + "\n");
1758 } else {
1759 if (matcher.end() < tmpLine
1760 .length()) {
1761 cell.setText(tmpLine.substring(matcher.start(), matcher.end()) + "\n");
1762 }
1763 else {
1764 cell.setText(tmpLine.substring(matcher.start(), tmpLine.length()) + "\n");
1765 }
1766 }
1767 }
1768
1769 if (lastLines.size() == 0) {
1770 row.addAttribute("debug", "pCell");
1771 cell.addAttribute(CELL_END, TRUE);
1772 } else {
1773 row.addAttribute("debug", "pCellEnd");
1774 cell.addAttribute(CELL_END, FALSE);
1775 }
1776 cell.addAttribute(CELL_INDEX_START, String
1777 .valueOf(matcher.start() + 1));
1778 if (line.length() == matcher.end()) {
1779 cell.addAttribute(CELL_INDEX_END, String.valueOf(columns.get(columns.size() - 1)));
1780 }
1781 else {
1782 cell.addAttribute(CELL_INDEX_END, String.valueOf(matcher.end()));
1783 }
1784 }
1785
1786 if (matcher2.matches()) {
1787 separation++;
1788 row.addAttribute(ROW_END_HEADER, ""
1789 + (separation == 2));
1790 } else {
1791 row.addAttribute(ROW_END_HEADER, FALSE);
1792 }
1793
1794 result.add(row);
1795 row = DocumentHelper.createElement(ROW);
1796 done = true;
1797 }
1798 }
1799 if (!done && l.matches("^\\s*(.+ +)+.+\\s*$")) {
1800
1801 lastLines.addFirst(l);
1802
1803
1804 done = true;
1805 }
1806 if (!done) {
1807 log.warn("Bad table format line "
1808 + in.getLineNumber());
1809 }
1810 }
1811 }
1812 }
1813 }
1814 endPeek();
1815
1816 return result;
1817 }
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831 public Element peekBulletList() throws IOException {
1832 beginPeek();
1833
1834 Element result = null;
1835
1836 String line = in.readLine();
1837 if (line != null
1838 && line.matches("^\\s*[" + escapeRegex(BULLET_CHAR)
1839 + "] +\\S.*")) {
1840 int level = level(line);
1841 String bullet = line.substring(level, level + 1);
1842
1843 result = DocumentHelper.createElement(BULLET_LIST).addAttribute(
1844 LEVEL, String.valueOf(level)).addAttribute(BULLET,
1845 bullet);
1846
1847 if (!in.eof()) {
1848 String[] content = readBlock(level + 1);
1849 line += " " + joinBlock(content);
1850 }
1851 String text = line.substring(level + 1).trim();
1852
1853 result.addText(text);
1854 in.skipBlankLines();
1855 }
1856
1857 endPeek();
1858 return result;
1859 }
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875 public Element peekFieldList() throws IOException {
1876 beginPeek();
1877
1878 Element result = null;
1879
1880 String line = in.readLine();
1881 if (line != null) {
1882 Pattern pattern = Pattern.compile("^\\s*:([^:]+): [^\\s].*");
1883 Matcher matcher = pattern.matcher(line);
1884 if (matcher.matches()) {
1885 int level = level(line);
1886 String name = matcher.group(1);
1887 int begin = matcher.end(1) + 1;
1888
1889 result = DocumentHelper.createElement(FIELD_LIST).addAttribute(
1890 LEVEL, String.valueOf(level)).addAttribute(NAME,
1891 name);
1892
1893 if (!in.eof()) {
1894 String[] content = readBlock(level + 1);
1895 line += " " + joinBlock(content);
1896 }
1897 String text = line.substring(begin).trim();
1898
1899 result.addText(text);
1900 }
1901 }
1902
1903 endPeek();
1904 return result;
1905 }
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923 public Element peekDefinitionList() throws IOException {
1924 beginPeek();
1925
1926 Element result = null;
1927
1928 String[] lines = in.readLines(2);
1929 if (lines.length == 2) {
1930 int level = level(lines[0]);
1931 int levelDef = level(lines[1]);
1932 if ((levelDef != lines[1].length()) && (level < levelDef)) {
1933 in.unread(lines[1], true);
1934 Pattern pattern = Pattern.compile("^\\s*([^:]+)(?: : (.*))?");
1935 Matcher matcher = pattern.matcher(lines[0]);
1936 if (matcher.matches()) {
1937 String term = matcher.group(1);
1938 String classifiers = matcher.group(2);
1939
1940 result = DocumentHelper.createElement(DEFINITION_LIST)
1941 .addAttribute(LEVEL, String.valueOf(level))
1942 .addAttribute(TERM, term).addAttribute(
1943 CLASSIFIERS, classifiers);
1944
1945
1946
1947
1948
1949
1950
1951
1952 }
1953 }
1954 }
1955
1956 endPeek();
1957 return result;
1958 }
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980 public Element peekEnumeratedList() throws IOException {
1981 beginPeek();
1982
1983 Element result = null;
1984
1985
1986 String line = in.readLine();
1987 if (line != null) {
1988 Pattern pattern = Pattern
1989 .compile("^\\s*(\\(?)(#|\\d+|[a-z]|[A-Z]|[ivxlcdm]+|[IVXLCDM]+)([\\.)]) [^\\s].*");
1990 Matcher matcher = pattern.matcher(line);
1991 if (matcher.matches()) {
1992 int level = level(line);
1993 String prefix = matcher.group(1);
1994 String start = matcher.group(2);
1995 String suffix = matcher.group(3);
1996 int begin = matcher.end(3);
1997
1998
1999 String enumtype = "auto";
2000 if (start.matches("\\d+")) {
2001 enumtype = "arabic";
2002 } else if (start.matches("(i|[ivxlcdm][ivxlcdm]+)")) {
2003 enumtype = "lowerroman";
2004 start = "1";
2005 } else if (start.matches("(I|[IVXLCDM][IVXLCDM]+)")) {
2006 enumtype = "upperroman";
2007 start = "1";
2008 } else if (start.matches("[a-z]+")) {
2009 enumtype = "loweralpha";
2010 start = String.valueOf((int) start.charAt(0) - (int) 'a');
2011 } else if (start.matches("[A-Z]+")) {
2012 enumtype = "upperalpha";
2013 start = String.valueOf((int) start.charAt(0) - (int) 'A');
2014 }
2015
2016 result = DocumentHelper.createElement(ENUMERATED_LIST)
2017 .addAttribute(LEVEL, String.valueOf(level))
2018 .addAttribute(START, start).addAttribute(PREFIX,
2019 prefix).addAttribute(SUFFIX, suffix)
2020 .addAttribute(ENUMTYPE, enumtype);
2021
2022 if (line.endsWith(": ::")) {
2023 line = line.substring(0, line.length() - " ::".length());
2024 in.unread("::", true);
2025
2026 } else if (line.endsWith("::")) {
2027 line = line.substring(0, line.length() - ":".length());
2028
2029
2030 in.unread("::", true);
2031 }
2032
2033
2034 if (!in.eof()) {
2035 String[] content = readBlock(level + 1);
2036 String tempLine = " " + joinBlock(content);
2037
2038 if (tempLine.endsWith(": ::")) {
2039 tempLine = tempLine.substring(0, tempLine.length() - " ::".length());
2040 in.unread("::", true);
2041
2042 } else if (tempLine.endsWith("::")) {
2043 tempLine = tempLine.substring(0, tempLine.length() - ":".length());
2044
2045
2046 in.unread("::", true);
2047 }
2048
2049 line+=tempLine;
2050
2051
2052 }
2053 String text = line.substring(begin).trim();
2054
2055 result.addText(text);
2056 in.skipBlankLines();
2057 }
2058 }
2059
2060 endPeek();
2061 return result;
2062 }
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087 public Element peekTitle() throws IOException {
2088 beginPeek();
2089
2090 Element result = null;
2091
2092 String line = in.readLine();
2093
2094 if (line != null) {
2095 if (startsWithTitleChar(line)) {
2096 String[] titles = in.readLines(2);
2097 if (titles.length == 2 && line.length() >= titles[0].length()
2098 && line.length() == titles[1].length()
2099 && line.equals(titles[1])) {
2100 result = DocumentHelper.createElement(TITLE).addAttribute(
2101 TYPE, "double").addAttribute(CHAR,
2102 titles[1].substring(0, 1)).addText(titles[0]);
2103 }
2104 } else {
2105 String title = in.readLine();
2106 if (title != null
2107 && startsWithTitleChar(title)
2108 && line.replaceFirst("\\s*$", "").length() == title
2109 .length()) {
2110
2111 result = DocumentHelper.createElement(TITLE).addAttribute(
2112 TYPE, "simple").addAttribute(CHAR,
2113 title.substring(0, 1)).addText(
2114 line.replaceFirst("\\s*$", ""));
2115 }
2116 }
2117 }
2118
2119 if (result != null) {
2120
2121 String titleLevel = result.attributeValue(CHAR);
2122
2123 if ("double".equals(result.attributeValue(TYPE))) {
2124 titleLevel += titleLevel;
2125 }
2126 int level = titleLevels.indexOf(titleLevel);
2127 if (level == -1) {
2128 level = titleLevels.size();
2129 titleLevels.add(titleLevel);
2130 }
2131 result.addAttribute(LEVEL, String
2132 .valueOf(JRSTReader.MAX_SECTION_DEPTH + level));
2133 }
2134
2135 endPeek();
2136 return result;
2137 }
2138
2139 public Element peekTarget() throws IOException {
2140 beginPeek();
2141
2142 String line = in.readLine();
2143 Element result = null;
2144 if (line != null) {
2145 if (line.matches("^\\s*\\.\\.\\s_[^_:].+:.*")) {
2146 result = DocumentHelper.createElement(TARGET);
2147 Matcher matcher = Pattern.compile("\\.\\.\\s_").matcher(line);
2148 if (matcher.find()) {
2149 int i = line.indexOf(':');
2150 result.addAttribute(ID, URLEncoder.encode(line.substring(matcher.end(), i)
2151 .toLowerCase().replaceAll(" ", "-"), "UTF-8"));
2152 result.addAttribute(LEVEL, "" + level(line));
2153 }
2154 }
2155 }
2156 endPeek();
2157 return result;
2158 }
2159
2160
2161
2162
2163
2164
2165
2166 public LinkedList<Element> refTarget() throws IOException {
2167 beginPeek();
2168
2169 String[] lines = in.readAll();
2170 LinkedList<Element> result = new LinkedList<Element>();
2171 for (String line : lines) {
2172 if (line.matches("^\\s*\\.\\.\\s_[^_:].+:.*")) {
2173 result.add(DocumentHelper.createElement(TARGET));
2174 Matcher matcher = Pattern.compile("\\.\\.\\s_").matcher(line);
2175 if (matcher.find()) {
2176 boolean done = false;
2177 for (int i = matcher.end(); i < line.length() && !done; i++) {
2178 if (line.charAt(i) == ':') {
2179 result.getLast().addAttribute(LEVEL,
2180 "" + level(line));
2181 result.getLast().addAttribute(
2182 ID,
2183 URLEncoder.encode(line.substring(matcher.end(), i)
2184 .replaceAll(" ", "-").toLowerCase(), "UTF-8"));
2185 result.getLast().addAttribute(
2186 NAME,
2187 line.substring(matcher.end(), i)
2188 .toLowerCase());
2189 if (i + 2 > line.length()) {
2190 line = in.readLine();
2191
2192
2193
2194
2195 if (line == null) {
2196 line = "";
2197 }
2198 result.getLast().addAttribute(REFURI,
2199 line.trim());
2200 } else {
2201 result.getLast().addAttribute(REFURI, line.substring(i + 2, line.length()));
2202 }
2203
2204 done = true;
2205 }
2206 }
2207 }
2208 }
2209 }
2210 endPeek();
2211 return result;
2212 }
2213
2214
2215
2216
2217
2218
2219
2220 private Element peekTargetAnonymousBody() throws IOException {
2221 beginPeek();
2222 Element result = null;
2223 String line = in.readLine();
2224 if (line != null) {
2225 if (line.matches("^\\s*__ .+$|^\\s*\\.\\. __\\:.+$")) {
2226 result = DocumentHelper.createElement(TARGETANONYMOUS);
2227 result.addAttribute(LEVEL, "" + level(line));
2228
2229 }
2230 }
2231
2232 endPeek();
2233 return result;
2234 }
2235
2236
2237
2238
2239
2240
2241
2242 private Element peekComment() throws IOException {
2243 beginPeek();
2244 Element result = null;
2245 String line = in.readLine();
2246 if (line != null) {
2247 if (line.matches("^\\.\\.\\s+.*$")) {
2248 result = DocumentHelper.createElement(COMMENT);
2249 result.addAttribute(LEVEL, "0");
2250 result.addAttribute(XMLSPACE, "preserve");
2251
2252
2253 result.setText(line.substring(2).trim());
2254 line = in.readLine();
2255 if(line != null) {
2256 int level = level(line);
2257 if (level > 0) {
2258 String[] lines = readBlock(level);
2259 String text = line.substring(level);
2260 for (String l : lines) {
2261 text += "\n" + l.substring(level);
2262 }
2263 result.addText(text);
2264 }
2265 }
2266 }
2267 }
2268
2269 endPeek();
2270 return result;
2271 }
2272
2273
2274
2275
2276
2277
2278
2279 public List<Element> peekAllComment() throws IOException {
2280 beginPeek();
2281 List<Element> result = new ArrayList<Element>();
2282 String[] lines = in.readWhile("^\\.\\.\\s*.*$");
2283 if (lines != null) {
2284
2285 for (String line : lines) {
2286 Element comment = DocumentHelper.createElement(COMMENT);
2287 comment.addAttribute(LEVEL, "0");
2288 comment.addAttribute(XMLSPACE, "preserve");
2289
2290
2291 comment.setText(line.substring(2).trim());
2292 result.add(comment);
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303 }
2304 in.mark();
2305 }
2306
2307 endPeek();
2308 return result;
2309 }
2310
2311
2312
2313
2314
2315
2316
2317 public Element peekFootnote() throws IOException {
2318 beginPeek();
2319 Element result = null;
2320 String line = in.readLine();
2321 if (line != null) {
2322 if (line.matches("^\\s*\\.\\.\\s\\[(#|[0-9]|\\*).*\\]\\s.+$")) {
2323 result = DocumentHelper.createElement(FOOTNOTES);
2324 boolean bLine = false;
2325 do {
2326
2327 bLine = false;
2328 Element footnote = result.addElement(FOOTNOTE);
2329 Matcher matcher = Pattern.compile("\\.\\.\\s\\[").matcher(
2330 line);
2331
2332 if (matcher.find()) {
2333
2334 boolean done = false;
2335 for (int i = matcher.end(); i < line.length() && !done; i++) {
2336 if (line.charAt(i) == ']') {
2337
2338 result.addAttribute(LEVEL, "" + level(line));
2339 String id = line.substring(matcher.end(), i);
2340 if (id.matches("\\*")) {
2341 footnote.addAttribute(TYPE, AUTOSYMBOL);
2342 } else if (id.matches("[0-9]")) {
2343 footnote.addAttribute(TYPE, NUM);
2344 footnote.addAttribute(NAME, id);
2345 } else if (id.equals("#")) {
2346 footnote.addAttribute(TYPE, AUTONUM);
2347 } else {
2348 footnote.addAttribute(TYPE,
2349 AUTONUMLABEL);
2350 footnote.addAttribute(NAME, id
2351 .substring(1));
2352 }
2353 String text = line.substring(i + 2, line
2354 .length());
2355
2356 int levelAv = level(line);
2357 line = in.readLine();
2358 if (line != null) {
2359 if (line
2360 .matches("^\\s*\\.\\.\\s\\[(#|[0-9]|\\*).*\\]\\s.+$")) {
2361 bLine = true;
2362 } else {
2363
2364 int level = level(line);
2365 if (levelAv < level) {
2366 String[] lines = in
2367 .readWhile("(^ {" + level
2368 + "}.*)|(\\s*)");
2369 text += "\n" + line.trim();
2370 for (String l : lines) {
2371 text += "\n" + l.trim();
2372 }
2373
2374 } else if (line.matches("\\s*")) {
2375 level = levelAv + 1;
2376 String[] lines = in
2377 .readWhile("(^ {" + level
2378 + "}.*)|(\\s*)");
2379 text += "\n" + line.trim();
2380 for (String l : lines) {
2381 text += "\n" + l.trim();
2382 }
2383
2384 }
2385 }
2386 if (!bLine) {
2387 in.skipBlankLines();
2388 String[] linesTmp = in
2389 .readWhile("^\\s*\\.\\.\\s\\[(#|[0-9]|\\*).*\\]\\s.+$");
2390
2391 if (linesTmp.length > 0) {
2392 line = linesTmp[0];
2393 bLine = true;
2394 }
2395 }
2396
2397 }
2398 if (line == null) {
2399 line = "";
2400 }
2401 footnote.setText(text);
2402 done = true;
2403 }
2404
2405 }
2406 }
2407
2408 } while (bLine);
2409 }
2410 }
2411 endPeek();
2412 return result;
2413 }
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423 private String readBlockWithBlankLine(int level) throws IOException {
2424 String txt = "";
2425 String[] lines = in.readWhile("(^ {" + level + "}.*)|(\\s*)");
2426 while (lines.length > 0) {
2427 for (String l : lines) {
2428 l = l.trim();
2429 txt += l + "\n";
2430
2431 }
2432 lines = in.readWhile("(^ {" + level + "}.*)|(\\s*)");
2433 }
2434 return txt;
2435 }
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445 public String readNotBlanckLine() throws IOException {
2446 beginPeek();
2447 in.skipBlankLines();
2448 String line = joinBlock(in.readUntilBlank(), "\n", false);
2449 endPeek();
2450 return line;
2451 }
2452
2453
2454
2455
2456
2457
2458 public int getLineNumber() {
2459 return in.getLineNumber();
2460 }
2461
2462
2463
2464
2465
2466
2467 public int getCharNumber() {
2468 return in.getCharNumber();
2469 }
2470
2471
2472
2473
2474
2475
2476
2477 private boolean startsWithTitleChar(String line) {
2478 if (line == null || line.length() < 2) {
2479 return false;
2480 }
2481
2482
2483 boolean result = line
2484 .matches("([" + escapeRegex(TITLE_CHAR) + "])\\1+");
2485 return result;
2486 }
2487
2488
2489
2490
2491
2492 private String escapeRegex(String text) {
2493 String result = text.replaceAll("([()[\\\\]*+?.])", "\\\\$1");
2494 return result;
2495 }
2496
2497
2498
2499
2500
2501
2502
2503 private int level(String line) {
2504 int result = 0;
2505 String sTmp = line.replaceAll("\\s", " ");
2506 while (sTmp.length() > result && sTmp.charAt(result) == ' ') {
2507 result++;
2508 }
2509 return result;
2510 }
2511 }