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