1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.doxia.module.apt;
20
21 import javax.swing.text.MutableAttributeSet;
22
23 import java.io.PrintWriter;
24 import java.io.Writer;
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.LinkedList;
28 import java.util.List;
29 import java.util.Stack;
30
31 import org.apache.maven.doxia.sink.Sink;
32 import org.apache.maven.doxia.sink.SinkEventAttributes;
33 import org.apache.maven.doxia.sink.impl.AbstractTextSink;
34 import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
35 import org.apache.maven.doxia.sink.impl.SinkUtils;
36 import org.apache.maven.doxia.util.DoxiaStringUtils;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40
41
42
43
44
45
46
47
48 public class AptSink extends AbstractTextSink implements AptMarkup {
49 private static final Logger LOGGER = LoggerFactory.getLogger(AptSink.class);
50
51
52
53
54
55
56 private StringBuffer buffer;
57
58
59 private StringBuilder tableCaptionBuffer;
60
61
62 private Collection<String> authors;
63
64
65 private String title;
66
67
68 private String date;
69
70
71 private boolean startFlag;
72
73
74 private boolean tableCaptionFlag;
75
76
77 private boolean tableCellFlag;
78
79
80 private boolean headerFlag;
81
82
83 private boolean bufferFlag;
84
85
86 private boolean itemFlag;
87
88
89 private boolean verbatimFlag;
90
91
92 private boolean isSource;
93
94
95 private boolean gridFlag;
96
97
98 private int cellCount;
99
100
101 private final PrintWriter writer;
102
103
104 private int[] cellJustif;
105
106
107 private String rowLine;
108
109
110 private String listNestingIndent;
111
112
113 private final Stack<String> listStyles;
114
115
116 protected Stack<List<String>> inlineStack = new Stack<>();
117
118
119
120
121
122
123
124
125
126
127 protected AptSink(Writer writer) {
128 this.writer = new PrintWriter(writer);
129 this.listStyles = new Stack<>();
130
131 init();
132 }
133
134
135
136
137
138
139 protected StringBuffer getBuffer() {
140 return buffer;
141 }
142
143
144
145
146
147
148 protected void setHeadFlag(boolean headFlag) {
149 this.headerFlag = headFlag;
150 }
151
152
153
154
155 protected void init() {
156 super.init();
157
158 resetBuffer();
159
160 this.tableCaptionBuffer = new StringBuilder();
161 this.listNestingIndent = "";
162
163 this.authors = new LinkedList<>();
164 this.title = null;
165 this.date = null;
166 this.startFlag = true;
167 this.tableCaptionFlag = false;
168 this.tableCellFlag = false;
169 this.headerFlag = false;
170 this.bufferFlag = false;
171 this.itemFlag = false;
172 this.verbatimFlag = false;
173 this.isSource = false;
174 this.gridFlag = false;
175 this.cellCount = 0;
176 this.cellJustif = null;
177 this.rowLine = null;
178 this.listStyles.clear();
179 this.inlineStack.clear();
180 }
181
182
183
184
185 protected void resetBuffer() {
186 buffer = new StringBuffer();
187 }
188
189
190
191
192 protected void resetTableCaptionBuffer() {
193 tableCaptionBuffer = new StringBuilder();
194 }
195
196 @Override
197 public void head(SinkEventAttributes attributes) {
198 boolean startFlag = this.startFlag;
199
200 init();
201
202 headerFlag = true;
203 this.startFlag = startFlag;
204 }
205
206
207
208
209 public void head_() {
210 headerFlag = false;
211
212 write(HEADER_START_MARKUP + EOL);
213 if (title != null) {
214 write(" " + title + EOL);
215 }
216 write(HEADER_START_MARKUP + EOL);
217 for (String author : authors) {
218 write(" " + author + EOL);
219 }
220 write(HEADER_START_MARKUP + EOL);
221 if (date != null) {
222 write(" " + date + EOL);
223 }
224 write(HEADER_START_MARKUP + EOL);
225 }
226
227
228
229
230 public void title_() {
231 if (buffer.length() > 0) {
232 title = buffer.toString();
233 resetBuffer();
234 }
235 }
236
237
238
239
240 public void author_() {
241 if (buffer.length() > 0) {
242 authors.add(buffer.toString());
243 resetBuffer();
244 }
245 }
246
247
248
249
250 public void date_() {
251 if (buffer.length() > 0) {
252 date = buffer.toString();
253 resetBuffer();
254 }
255 }
256
257 @Override
258 public void section_(int level) {
259 write(EOL);
260 }
261
262 @Override
263 public void sectionTitle(int level, SinkEventAttributes attributes) {
264 if (level > 5) {
265 LOGGER.warn(
266 "{}Replacing unsupported section title level {} in APT with level 5",
267 getLocationLogPrefix(),
268 level);
269 level = 5;
270 }
271 if (level == 1) {
272 write(EOL);
273 } else if (level > 1) {
274 write(EOL + DoxiaStringUtils.repeat(SECTION_TITLE_START_MARKUP, level - 1));
275 }
276 }
277
278 @Override
279 public void sectionTitle_(int level) {
280 if (level >= 1) {
281 write(EOL + EOL);
282 }
283 }
284
285 @Override
286 public void list(SinkEventAttributes attributes) {
287 listNestingIndent += " ";
288 listStyles.push(LIST_START_MARKUP);
289 write(EOL);
290 }
291
292
293
294
295 public void list_() {
296 if (listNestingIndent.length() <= 1) {
297 write(EOL + listNestingIndent + LIST_END_MARKUP + EOL);
298 } else {
299 write(EOL);
300 }
301 listNestingIndent = DoxiaStringUtils.removeEnd(listNestingIndent, " ");
302 listStyles.pop();
303 itemFlag = false;
304 }
305
306 @Override
307 public void listItem(SinkEventAttributes attributes) {
308
309
310
311 numberedListItem();
312 itemFlag = true;
313 }
314
315
316
317
318 public void listItem_() {
319 write(EOL);
320 itemFlag = false;
321 }
322
323 @Override
324 public void numberedList(int numbering, SinkEventAttributes attributes) {
325 listNestingIndent += " ";
326 write(EOL);
327
328 String style;
329 switch (numbering) {
330 case NUMBERING_UPPER_ALPHA:
331 style = String.valueOf(NUMBERING_UPPER_ALPHA_CHAR);
332 break;
333 case NUMBERING_LOWER_ALPHA:
334 style = String.valueOf(NUMBERING_LOWER_ALPHA_CHAR);
335 break;
336 case NUMBERING_UPPER_ROMAN:
337 style = String.valueOf(NUMBERING_UPPER_ROMAN_CHAR);
338 break;
339 case NUMBERING_LOWER_ROMAN:
340 style = String.valueOf(NUMBERING_LOWER_ROMAN_CHAR);
341 break;
342 case NUMBERING_DECIMAL:
343 default:
344 style = String.valueOf(NUMBERING);
345 }
346
347 listStyles.push(style);
348 }
349
350
351
352
353 public void numberedList_() {
354 if (listNestingIndent.length() <= 1) {
355 write(EOL + listNestingIndent + LIST_END_MARKUP + EOL);
356 } else {
357 write(EOL);
358 }
359 listNestingIndent = DoxiaStringUtils.removeEnd(listNestingIndent, " ");
360 listStyles.pop();
361 itemFlag = false;
362 }
363
364 @Override
365 public void numberedListItem(SinkEventAttributes attributes) {
366 String style = listStyles.peek();
367 if (style.equals(String.valueOf(STAR))) {
368 write(EOL + listNestingIndent + STAR + SPACE);
369 } else {
370 write(EOL
371 + listNestingIndent
372 + LEFT_SQUARE_BRACKET
373 + LEFT_SQUARE_BRACKET
374 + style
375 + RIGHT_SQUARE_BRACKET
376 + RIGHT_SQUARE_BRACKET
377 + SPACE);
378 }
379 itemFlag = true;
380 }
381
382
383
384
385 public void numberedListItem_() {
386 write(EOL);
387 itemFlag = false;
388 }
389
390 @Override
391 public void definitionList(SinkEventAttributes attributes) {
392 listNestingIndent += " ";
393 listStyles.push("");
394 write(EOL);
395 }
396
397
398
399
400 public void definitionList_() {
401 if (listNestingIndent.length() <= 1) {
402 write(EOL + listNestingIndent + LIST_END_MARKUP + EOL);
403 } else {
404 write(EOL);
405 }
406 listNestingIndent = DoxiaStringUtils.removeEnd(listNestingIndent, " ");
407 listStyles.pop();
408 itemFlag = false;
409 }
410
411 @Override
412 public void definedTerm(SinkEventAttributes attributes) {
413 write(EOL + " [");
414 }
415
416
417
418
419 public void definedTerm_() {
420 write("] ");
421 }
422
423 @Override
424 public void definition(SinkEventAttributes attributes) {
425 itemFlag = true;
426 }
427
428
429
430
431 public void definition_() {
432 write(EOL);
433 itemFlag = false;
434 }
435
436
437
438
439 public void pageBreak() {
440 write(EOL + PAGE_BREAK + EOL);
441 }
442
443 @Override
444 public void paragraph(SinkEventAttributes attributes) {
445 if (tableCellFlag) {
446
447 } else if (itemFlag) {
448 write(EOL + EOL + " " + listNestingIndent);
449 } else {
450 write(EOL + " ");
451 }
452 }
453
454
455
456
457 public void paragraph_() {
458 if (tableCellFlag) {
459
460 } else {
461 write(EOL + EOL);
462 }
463 }
464
465 @Override
466 public void verbatim(SinkEventAttributes attributes) {
467 MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_VERBATIM_ATTRIBUTES);
468
469 boolean source = false;
470
471 if (atts != null && atts.isDefined(SinkEventAttributes.DECORATION)) {
472 source = "source"
473 .equals(atts.getAttribute(SinkEventAttributes.DECORATION).toString());
474 }
475
476 verbatimFlag = true;
477 this.isSource = source;
478 write(EOL);
479 if (source) {
480 write(EOL + VERBATIM_SOURCE_START_MARKUP + EOL);
481 } else {
482 write(EOL + VERBATIM_START_MARKUP + EOL);
483 }
484 }
485
486
487
488
489 public void verbatim_() {
490 if (isSource) {
491 write(EOL + VERBATIM_SOURCE_END_MARKUP + EOL);
492 } else {
493 write(EOL + VERBATIM_END_MARKUP + EOL);
494 }
495 isSource = false;
496 verbatimFlag = false;
497 }
498
499 @Override
500 public void horizontalRule(SinkEventAttributes attributes) {
501 write(EOL + HORIZONTAL_RULE_MARKUP + EOL);
502 }
503
504 @Override
505 public void table(SinkEventAttributes attributes) {
506 write(EOL);
507 }
508
509
510
511
512 public void table_() {
513 if (rowLine != null) {
514 write(rowLine);
515 }
516 rowLine = null;
517
518 if (tableCaptionBuffer.length() > 0) {
519 text(tableCaptionBuffer.toString() + EOL);
520 }
521
522 resetTableCaptionBuffer();
523 }
524
525 @Override
526 public void tableRows(int[] justification, boolean grid) {
527 cellJustif = justification;
528 gridFlag = grid;
529 }
530
531
532
533
534 public void tableRows_() {
535 cellJustif = null;
536 gridFlag = false;
537 }
538
539 @Override
540 public void tableRow(SinkEventAttributes attributes) {
541 bufferFlag = true;
542 cellCount = 0;
543 }
544
545
546
547
548 public void tableRow_() {
549 bufferFlag = false;
550
551
552 buildRowLine();
553
554 write(rowLine);
555
556
557 if (gridFlag) {
558 write(TABLE_ROW_SEPARATOR_MARKUP);
559 }
560
561 write(buffer.toString());
562
563 resetBuffer();
564
565 write(EOL);
566
567
568 cellCount = 0;
569 }
570
571
572 private void buildRowLine() {
573 StringBuilder rLine = new StringBuilder();
574 rLine.append(TABLE_ROW_START_MARKUP);
575
576 for (int i = 0; i < cellCount; i++) {
577 if (cellJustif != null && i < cellJustif.length) {
578 switch (cellJustif[i]) {
579 case Sink.JUSTIFY_LEFT:
580 rLine.append(TABLE_COL_LEFT_ALIGNED_MARKUP);
581 break;
582 case Sink.JUSTIFY_RIGHT:
583 rLine.append(TABLE_COL_RIGHT_ALIGNED_MARKUP);
584 break;
585 default:
586
587 rLine.append(TABLE_COL_CENTERED_ALIGNED_MARKUP);
588 }
589 } else {
590 rLine.append(TABLE_COL_CENTERED_ALIGNED_MARKUP);
591 }
592 }
593 rLine.append(EOL);
594
595 this.rowLine = rLine.toString();
596 }
597
598 @Override
599 public void tableCell(SinkEventAttributes attributes) {
600 tableCell(false);
601 }
602
603 @Override
604 public void tableHeaderCell(SinkEventAttributes attributes) {
605 tableCell(true);
606 }
607
608
609
610
611
612
613 public void tableCell(boolean headerRow) {
614 if (headerRow) {
615 buffer.append(TABLE_CELL_SEPARATOR_MARKUP);
616 }
617 tableCellFlag = true;
618 }
619
620
621
622
623 public void tableCell_() {
624 endTableCell();
625 }
626
627
628
629
630 public void tableHeaderCell_() {
631 endTableCell();
632 }
633
634
635
636
637 private void endTableCell() {
638 tableCellFlag = false;
639 buffer.append(TABLE_CELL_SEPARATOR_MARKUP);
640 cellCount++;
641 }
642
643 @Override
644 public void tableCaption(SinkEventAttributes attributes) {
645 tableCaptionFlag = true;
646 }
647
648
649
650
651 public void tableCaption_() {
652 tableCaptionFlag = false;
653 }
654
655
656
657
658 public void figureCaption_() {
659 write(EOL);
660 }
661
662 @Override
663 public void figureGraphics(String name, SinkEventAttributes attributes) {
664 write(EOL + "[" + name + "] ");
665 }
666
667 @Override
668 public void anchor(String name, SinkEventAttributes attributes) {
669 write(ANCHOR_START_MARKUP);
670 }
671
672
673
674
675 public void anchor_() {
676 write(ANCHOR_END_MARKUP);
677 }
678
679 @Override
680 public void link(String name, SinkEventAttributes attributes) {
681 if (!headerFlag) {
682 write(LINK_START_1_MARKUP);
683 text(name.startsWith("#") ? name.substring(1) : name);
684 write(LINK_START_2_MARKUP);
685 }
686 }
687
688
689
690
691 public void link_() {
692 if (!headerFlag) {
693 write(LINK_END_MARKUP);
694 }
695 }
696
697
698
699
700
701
702
703 public void link(String name, String target) {
704 if (!headerFlag) {
705 write(LINK_START_1_MARKUP);
706 text(target);
707 write(LINK_START_2_MARKUP);
708 text(name);
709 }
710 }
711
712 public void inline(SinkEventAttributes attributes) {
713 if (!headerFlag) {
714 List<String> tags = new ArrayList<>();
715
716 if (attributes != null) {
717
718 if (attributes.containsAttribute(SinkEventAttributes.SEMANTICS, "italic")) {
719 write(ITALIC_START_MARKUP);
720 tags.add(0, ITALIC_END_MARKUP);
721 }
722
723 if (attributes.containsAttribute(SinkEventAttributes.SEMANTICS, "bold")) {
724 write(BOLD_START_MARKUP);
725 tags.add(0, BOLD_END_MARKUP);
726 }
727
728 if (attributes.containsAttribute(SinkEventAttributes.SEMANTICS, "code")) {
729 write(MONOSPACED_START_MARKUP);
730 tags.add(0, MONOSPACED_END_MARKUP);
731 }
732 }
733
734 inlineStack.push(tags);
735 }
736 }
737
738
739
740
741 public void inline_() {
742 if (!headerFlag) {
743 for (String tag : inlineStack.pop()) {
744 write(tag);
745 }
746 }
747 }
748
749
750
751
752 public void italic() {
753 inline(SinkEventAttributeSet.Semantics.ITALIC);
754 }
755
756
757
758
759 public void italic_() {
760 inline_();
761 }
762
763
764
765
766 public void bold() {
767 inline(SinkEventAttributeSet.Semantics.BOLD);
768 }
769
770
771
772
773 public void bold_() {
774 inline_();
775 }
776
777
778
779
780 public void monospaced() {
781 inline(SinkEventAttributeSet.Semantics.CODE);
782 }
783
784
785
786
787 public void monospaced_() {
788 inline_();
789 }
790
791 @Override
792 public void lineBreak(SinkEventAttributes attributes) {
793 if (headerFlag || bufferFlag) {
794 buffer.append(EOL);
795 } else if (verbatimFlag) {
796 write(EOL);
797 } else {
798 write("\\" + EOL);
799 }
800 }
801
802
803
804
805 public void nonBreakingSpace() {
806 if (headerFlag || bufferFlag) {
807 buffer.append(NON_BREAKING_SPACE_MARKUP);
808 } else {
809 write(NON_BREAKING_SPACE_MARKUP);
810 }
811 }
812
813 @Override
814 public void text(String text, SinkEventAttributes attributes) {
815 if (attributes != null) {
816 inline(attributes);
817 }
818 if (tableCaptionFlag) {
819 tableCaptionBuffer.append(text);
820 } else if (headerFlag || bufferFlag) {
821 buffer.append(text);
822 } else if (verbatimFlag) {
823 verbatimContent(text);
824 } else {
825 content(text);
826 }
827 if (attributes != null) {
828 inline_();
829 }
830 }
831
832 public void rawText(String text) {
833 write(text);
834 }
835
836 public void comment(String comment) {
837 rawText("" + COMMENT + COMMENT + comment + EOL);
838 }
839
840
841
842
843
844
845
846 public void unknown(String name, Object[] requiredParams, SinkEventAttributes attributes) {
847 LOGGER.warn("{}Unknown Sink event '{}', ignoring!", getLocationLogPrefix(), name);
848 }
849
850
851
852
853
854
855 protected void write(String text) {
856 startFlag = false;
857 if (tableCellFlag) {
858 buffer.append(text);
859 } else {
860 writer.write(unifyEOLs(text));
861 }
862 }
863
864
865
866
867
868
869 protected void content(String text) {
870 write(escapeAPT(text));
871 }
872
873
874
875
876
877
878 protected void verbatimContent(String text) {
879 write(escapeAPT(text));
880 }
881
882
883
884
885 public void flush() {
886 writer.flush();
887 }
888
889
890
891
892 public void close() {
893 writer.close();
894
895 init();
896 }
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912 static String escapeAPT(String text) {
913 if (text == null) {
914 return "";
915 }
916
917 int length = text.length();
918 StringBuilder buffer = new StringBuilder(length);
919
920 for (int i = 0; i < length; ++i) {
921 char c = text.charAt(i);
922 switch (c) {
923 case '\\':
924 case '~':
925 case '=':
926 case '-':
927 case '+':
928 case '*':
929 case '[':
930 case ']':
931 case '<':
932 case '>':
933 case '{':
934 case '}':
935 buffer.append('\\');
936 buffer.append(c);
937 break;
938 default:
939 buffer.append(c);
940 }
941 }
942
943 return buffer.toString();
944 }
945 }