1 package org.apache.maven.doxia.module.apt;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.PrintWriter;
23 import java.io.Writer;
24 import java.util.ArrayList;
25 import java.util.List;
26 import java.util.Stack;
27
28 import javax.swing.text.MutableAttributeSet;
29
30 import org.apache.maven.doxia.sink.SinkEventAttributes;
31 import org.apache.maven.doxia.sink.impl.AbstractTextSink;
32 import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
33 import org.apache.maven.doxia.sink.impl.SinkUtils;
34 import org.codehaus.plexus.util.StringUtils;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38
39
40
41
42
43
44
45
46 public class AptSink
47 extends AbstractTextSink
48 implements AptMarkup
49 {
50 private static final Logger LOGGER = LoggerFactory.getLogger( AptSink.class );
51
52
53
54
55
56
57 private StringBuffer buffer;
58
59
60 private StringBuilder tableCaptionBuffer;
61
62
63 private String author;
64
65
66 private String title;
67
68
69 private String date;
70
71
72 private boolean startFlag;
73
74
75 private boolean tableCaptionFlag;
76
77
78 private boolean tableCellFlag;
79
80
81 private boolean headerFlag;
82
83
84 private boolean bufferFlag;
85
86
87 private boolean itemFlag;
88
89
90 private boolean verbatimFlag;
91
92
93 private boolean isBoxed;
94
95
96 private boolean gridFlag;
97
98
99 private int cellCount;
100
101
102 private final PrintWriter writer;
103
104
105 private int[] cellJustif;
106
107
108 private String rowLine;
109
110
111 private String listNestingIndent;
112
113
114 private final Stack<String> listStyles;
115
116
117 protected Stack<List<String>> inlineStack = new Stack<>();
118
119
120
121
122
123
124
125
126
127
128
129 protected AptSink( Writer writer )
130 {
131 this.writer = new PrintWriter( writer );
132 this.listStyles = new Stack<>();
133
134 init();
135 }
136
137
138
139
140
141
142 protected StringBuffer getBuffer()
143 {
144 return buffer;
145 }
146
147
148
149
150
151
152 protected void setHeadFlag( boolean headFlag )
153 {
154 this.headerFlag = headFlag;
155 }
156
157
158
159
160 protected void init()
161 {
162 super.init();
163
164 resetBuffer();
165
166 this.tableCaptionBuffer = new StringBuilder();
167 this.listNestingIndent = "";
168
169 this.author = null;
170 this.title = null;
171 this.date = null;
172 this.startFlag = true;
173 this.tableCaptionFlag = false;
174 this.tableCellFlag = false;
175 this.headerFlag = false;
176 this.bufferFlag = false;
177 this.itemFlag = false;
178 this.verbatimFlag = false;
179 this.isBoxed = false;
180 this.gridFlag = false;
181 this.cellCount = 0;
182 this.cellJustif = null;
183 this.rowLine = null;
184 this.listStyles.clear();
185 this.inlineStack.clear();
186 }
187
188
189
190
191 protected void resetBuffer()
192 {
193 buffer = new StringBuffer();
194 }
195
196
197
198
199 protected void resetTableCaptionBuffer()
200 {
201 tableCaptionBuffer = new StringBuilder();
202 }
203
204
205
206
207 public void head()
208 {
209 boolean startFlag = this.startFlag;
210
211 init();
212
213 headerFlag = true;
214 this.startFlag = startFlag;
215 }
216
217
218
219
220 public void head_()
221 {
222 headerFlag = false;
223
224 if ( ! startFlag )
225 {
226 write( EOL );
227 }
228 write( HEADER_START_MARKUP + EOL );
229 if ( title != null )
230 {
231 write( " " + title + EOL );
232 }
233 write( HEADER_START_MARKUP + EOL );
234 if ( author != null )
235 {
236 write( " " + author + EOL );
237 }
238 write( HEADER_START_MARKUP + EOL );
239 if ( date != null )
240 {
241 write( " " + date + EOL );
242 }
243 write( HEADER_START_MARKUP + EOL );
244 }
245
246
247
248
249 public void title_()
250 {
251 if ( buffer.length() > 0 )
252 {
253 title = buffer.toString();
254 resetBuffer();
255 }
256 }
257
258
259
260
261 public void author_()
262 {
263 if ( buffer.length() > 0 )
264 {
265 author = buffer.toString();
266 resetBuffer();
267 }
268 }
269
270
271
272
273 public void date_()
274 {
275 if ( buffer.length() > 0 )
276 {
277 date = buffer.toString();
278 resetBuffer();
279 }
280 }
281
282
283
284
285 public void section1_()
286 {
287 write( EOL );
288 }
289
290
291
292
293 public void section2_()
294 {
295 write( EOL );
296 }
297
298
299
300
301 public void section3_()
302 {
303 write( EOL );
304 }
305
306
307
308
309 public void section4_()
310 {
311 write( EOL );
312 }
313
314
315
316
317 public void section5_()
318 {
319 write( EOL );
320 }
321
322
323
324
325 public void sectionTitle1()
326 {
327 write( EOL );
328 }
329
330
331
332
333 public void sectionTitle1_()
334 {
335 write( EOL + EOL );
336 }
337
338
339
340
341 public void sectionTitle2()
342 {
343 write( EOL + SECTION_TITLE_START_MARKUP );
344 }
345
346
347
348
349 public void sectionTitle2_()
350 {
351 write( EOL + EOL );
352 }
353
354
355
356
357 public void sectionTitle3()
358 {
359 write( EOL + StringUtils.repeat( SECTION_TITLE_START_MARKUP, 2 ) );
360 }
361
362
363
364
365 public void sectionTitle3_()
366 {
367 write( EOL + EOL );
368 }
369
370
371
372
373 public void sectionTitle4()
374 {
375 write( EOL + StringUtils.repeat( SECTION_TITLE_START_MARKUP, 3 ) );
376 }
377
378
379
380
381 public void sectionTitle4_()
382 {
383 write( EOL + EOL );
384 }
385
386
387
388
389 public void sectionTitle5()
390 {
391 write( EOL + StringUtils.repeat( SECTION_TITLE_START_MARKUP, 4 ) );
392 }
393
394
395
396
397 public void sectionTitle5_()
398 {
399 write( EOL + EOL );
400 }
401
402
403
404
405 public void list()
406 {
407 listNestingIndent += " ";
408 listStyles.push( LIST_START_MARKUP );
409 write( EOL );
410 }
411
412
413
414
415 public void list_()
416 {
417 if ( listNestingIndent.length() <= 1 )
418 {
419 write( EOL + listNestingIndent + LIST_END_MARKUP + EOL );
420 }
421 else
422 {
423 write( EOL );
424 }
425 listNestingIndent = StringUtils.chomp( listNestingIndent, " " );
426 listStyles.pop();
427 itemFlag = false;
428 }
429
430
431
432
433 public void listItem()
434 {
435
436
437
438 numberedListItem();
439 itemFlag = true;
440 }
441
442
443
444
445 public void listItem_()
446 {
447 write( EOL );
448 itemFlag = false;
449 }
450
451
452 public void numberedList( int numbering )
453 {
454 listNestingIndent += " ";
455 write( EOL );
456
457 String style;
458 switch ( numbering )
459 {
460 case NUMBERING_UPPER_ALPHA:
461 style = String.valueOf( NUMBERING_UPPER_ALPHA_CHAR );
462 break;
463 case NUMBERING_LOWER_ALPHA:
464 style = String.valueOf( NUMBERING_LOWER_ALPHA_CHAR );
465 break;
466 case NUMBERING_UPPER_ROMAN:
467 style = String.valueOf( NUMBERING_UPPER_ROMAN_CHAR );
468 break;
469 case NUMBERING_LOWER_ROMAN:
470 style = String.valueOf( NUMBERING_LOWER_ROMAN_CHAR );
471 break;
472 case NUMBERING_DECIMAL:
473 default:
474 style = String.valueOf( NUMBERING );
475 }
476
477 listStyles.push( style );
478 }
479
480
481
482
483 public void numberedList_()
484 {
485 if ( listNestingIndent.length() <= 1 )
486 {
487 write( EOL + listNestingIndent + LIST_END_MARKUP + EOL );
488 }
489 else
490 {
491 write( EOL );
492 }
493 listNestingIndent = StringUtils.chomp( listNestingIndent, " " );
494 listStyles.pop();
495 itemFlag = false;
496 }
497
498
499
500
501 public void numberedListItem()
502 {
503 String style = listStyles.peek();
504 if ( style.equals( String.valueOf( STAR ) ) )
505 {
506 write( EOL + listNestingIndent + STAR + SPACE );
507 }
508 else
509 {
510 write( EOL + listNestingIndent + LEFT_SQUARE_BRACKET
511 + LEFT_SQUARE_BRACKET + style + RIGHT_SQUARE_BRACKET
512 + RIGHT_SQUARE_BRACKET + SPACE );
513 }
514 itemFlag = true;
515 }
516
517
518
519
520 public void numberedListItem_()
521 {
522 write( EOL );
523 itemFlag = false;
524 }
525
526
527
528
529 public void definitionList()
530 {
531 listNestingIndent += " ";
532 listStyles.push( "" );
533 write( EOL );
534 }
535
536
537
538
539 public void definitionList_()
540 {
541 if ( listNestingIndent.length() <= 1 )
542 {
543 write( EOL + listNestingIndent + LIST_END_MARKUP + EOL );
544 }
545 else
546 {
547 write( EOL );
548 }
549 listNestingIndent = StringUtils.chomp( listNestingIndent, " " );
550 listStyles.pop();
551 itemFlag = false;
552 }
553
554
555
556
557 public void definedTerm()
558 {
559 write( EOL + " [" );
560 }
561
562
563
564
565 public void definedTerm_()
566 {
567 write( "] " );
568 }
569
570
571
572
573 public void definition()
574 {
575 itemFlag = true;
576 }
577
578
579
580
581 public void definition_()
582 {
583 write( EOL );
584 itemFlag = false;
585 }
586
587
588
589
590 public void pageBreak()
591 {
592 write( EOL + PAGE_BREAK + EOL );
593 }
594
595
596
597
598 public void paragraph()
599 {
600 if ( tableCellFlag )
601 {
602
603 }
604 else if ( itemFlag )
605 {
606 write( EOL + EOL + " " + listNestingIndent );
607 }
608 else
609 {
610 write( EOL + " " );
611 }
612 }
613
614
615
616
617 public void paragraph_()
618 {
619 if ( tableCellFlag )
620 {
621
622 }
623 else
624 {
625 write( EOL + EOL );
626 }
627 }
628
629
630 public void verbatim( SinkEventAttributes attributes )
631 {
632 MutableAttributeSet atts = SinkUtils.filterAttributes( attributes, SinkUtils.SINK_VERBATIM_ATTRIBUTES );
633
634 boolean boxed = false;
635
636 if ( atts != null && atts.isDefined( SinkEventAttributes.DECORATION ) )
637 {
638 boxed = "boxed".equals( atts.getAttribute( SinkEventAttributes.DECORATION ).toString() );
639 }
640
641 verbatimFlag = true;
642 this.isBoxed = boxed;
643 write( EOL );
644 if ( boxed )
645 {
646 write( EOL + BOXED_VERBATIM_START_MARKUP + EOL );
647 }
648 else
649 {
650 write( EOL + NON_BOXED_VERBATIM_START_MARKUP + EOL );
651 }
652 }
653
654
655
656
657 public void verbatim_()
658 {
659 if ( isBoxed )
660 {
661 write( EOL + BOXED_VERBATIM_END_MARKUP + EOL );
662 }
663 else
664 {
665 write( EOL + NON_BOXED_VERBATIM_END_MARKUP + EOL );
666 }
667 isBoxed = false;
668 verbatimFlag = false;
669 }
670
671
672
673
674 public void horizontalRule()
675 {
676 write( EOL + HORIZONTAL_RULE_MARKUP + EOL );
677 }
678
679
680
681
682 public void table()
683 {
684 write( EOL );
685 }
686
687
688
689
690 public void table_()
691 {
692 if ( rowLine != null )
693 {
694 write( rowLine );
695 }
696 rowLine = null;
697
698 if ( tableCaptionBuffer.length() > 0 )
699 {
700 text( tableCaptionBuffer.toString() + EOL );
701 }
702
703 resetTableCaptionBuffer();
704 }
705
706
707 public void tableRows( int[] justification, boolean grid )
708 {
709 cellJustif = justification;
710 gridFlag = grid;
711 }
712
713
714
715
716 public void tableRows_()
717 {
718 cellJustif = null;
719 gridFlag = false;
720 }
721
722
723
724
725 public void tableRow()
726 {
727 bufferFlag = true;
728 cellCount = 0;
729 }
730
731
732
733
734 public void tableRow_()
735 {
736 bufferFlag = false;
737
738
739 buildRowLine();
740
741 write( rowLine );
742
743
744 if ( gridFlag )
745 {
746 write( TABLE_ROW_SEPARATOR_MARKUP );
747 }
748
749 write( buffer.toString() );
750
751 resetBuffer();
752
753 write( EOL );
754
755
756 cellCount = 0;
757 }
758
759
760 private void buildRowLine()
761 {
762 StringBuilder rLine = new StringBuilder();
763 rLine.append( TABLE_ROW_START_MARKUP );
764
765 for ( int i = 0; i < cellCount; i++ )
766 {
767 if ( cellJustif != null )
768 {
769 switch ( cellJustif[i] )
770 {
771 case 1:
772 rLine.append( TABLE_COL_LEFT_ALIGNED_MARKUP );
773 break;
774 case 2:
775 rLine.append( TABLE_COL_RIGHT_ALIGNED_MARKUP );
776 break;
777 default:
778 rLine.append( TABLE_COL_CENTERED_ALIGNED_MARKUP );
779 }
780 }
781 else
782 {
783 rLine.append( TABLE_COL_CENTERED_ALIGNED_MARKUP );
784 }
785 }
786 rLine.append( EOL );
787
788 this.rowLine = rLine.toString();
789 }
790
791
792
793
794 public void tableCell()
795 {
796 tableCell( false );
797 }
798
799
800
801
802 public void tableHeaderCell()
803 {
804 tableCell( true );
805 }
806
807
808
809
810
811
812 public void tableCell( boolean headerRow )
813 {
814 if ( headerRow )
815 {
816 buffer.append( TABLE_CELL_SEPARATOR_MARKUP );
817 }
818 tableCellFlag = true;
819 }
820
821
822
823
824 public void tableCell_()
825 {
826 endTableCell();
827 }
828
829
830
831
832 public void tableHeaderCell_()
833 {
834 endTableCell();
835 }
836
837
838
839
840 private void endTableCell()
841 {
842 tableCellFlag = false;
843 buffer.append( TABLE_CELL_SEPARATOR_MARKUP );
844 cellCount++;
845 }
846
847
848
849
850 public void tableCaption()
851 {
852 tableCaptionFlag = true;
853 }
854
855
856
857
858 public void tableCaption_()
859 {
860 tableCaptionFlag = false;
861 }
862
863
864
865
866 public void figureCaption_()
867 {
868 write( EOL );
869 }
870
871
872 public void figureGraphics( String name )
873 {
874 write( EOL + "[" + name + "] " );
875 }
876
877
878 public void anchor( String name )
879 {
880 write( ANCHOR_START_MARKUP );
881 }
882
883
884
885
886 public void anchor_()
887 {
888 write( ANCHOR_END_MARKUP );
889 }
890
891
892 public void link( String name )
893 {
894 if ( !headerFlag )
895 {
896 write( LINK_START_1_MARKUP );
897 text( name.startsWith( "#" ) ? name.substring( 1 ) : name );
898 write( LINK_START_2_MARKUP );
899 }
900 }
901
902
903
904
905 public void link_()
906 {
907 if ( !headerFlag )
908 {
909 write( LINK_END_MARKUP );
910 }
911 }
912
913
914
915
916
917
918
919 public void link( String name, String target )
920 {
921 if ( !headerFlag )
922 {
923 write( LINK_START_1_MARKUP );
924 text( target );
925 write( LINK_START_2_MARKUP );
926 text( name );
927 }
928 }
929
930
931
932
933 public void inline()
934 {
935 inline( null );
936 }
937
938
939 public void inline( SinkEventAttributes attributes )
940 {
941 if ( !headerFlag )
942 {
943 List<String> tags = new ArrayList<>();
944
945 if ( attributes != null )
946 {
947
948 if ( attributes.containsAttribute( SinkEventAttributes.SEMANTICS, "italic" ) )
949 {
950 write( ITALIC_START_MARKUP );
951 tags.add( 0, ITALIC_END_MARKUP );
952 }
953
954 if ( attributes.containsAttribute( SinkEventAttributes.SEMANTICS, "bold" ) )
955 {
956 write( BOLD_START_MARKUP );
957 tags.add( 0, BOLD_END_MARKUP );
958 }
959
960 if ( attributes.containsAttribute( SinkEventAttributes.SEMANTICS, "code" ) )
961 {
962 write( MONOSPACED_START_MARKUP );
963 tags.add( 0, MONOSPACED_END_MARKUP );
964 }
965
966 }
967
968 inlineStack.push( tags );
969 }
970 }
971
972
973
974
975 public void inline_()
976 {
977 if ( !headerFlag )
978 {
979 for ( String tag: inlineStack.pop() )
980 {
981 write( tag );
982 }
983 }
984 }
985
986
987
988
989 public void italic()
990 {
991 inline( SinkEventAttributeSet.Semantics.ITALIC );
992 }
993
994
995
996
997 public void italic_()
998 {
999 inline_();
1000 }
1001
1002
1003
1004
1005 public void bold()
1006 {
1007 inline( SinkEventAttributeSet.Semantics.BOLD );
1008 }
1009
1010
1011
1012
1013 public void bold_()
1014 {
1015 inline_();
1016 }
1017
1018
1019
1020
1021 public void monospaced()
1022 {
1023 inline( SinkEventAttributeSet.Semantics.CODE );
1024 }
1025
1026
1027
1028
1029 public void monospaced_()
1030 {
1031 inline_();
1032 }
1033
1034
1035
1036
1037 public void lineBreak()
1038 {
1039 if ( headerFlag || bufferFlag )
1040 {
1041 buffer.append( EOL );
1042 }
1043 else if ( verbatimFlag )
1044 {
1045 write( EOL );
1046 }
1047 else
1048 {
1049 write( "\\" + EOL );
1050 }
1051 }
1052
1053
1054
1055
1056 public void nonBreakingSpace()
1057 {
1058 if ( headerFlag || bufferFlag )
1059 {
1060 buffer.append( NON_BREAKING_SPACE_MARKUP );
1061 }
1062 else
1063 {
1064 write( NON_BREAKING_SPACE_MARKUP );
1065 }
1066 }
1067
1068
1069 public void text( String text )
1070 {
1071 if ( tableCaptionFlag )
1072 {
1073 tableCaptionBuffer.append( text );
1074 }
1075 else if ( headerFlag || bufferFlag )
1076 {
1077 buffer.append( text );
1078 }
1079 else if ( verbatimFlag )
1080 {
1081 verbatimContent( text );
1082 }
1083 else
1084 {
1085 content( text );
1086 }
1087 }
1088
1089
1090 public void rawText( String text )
1091 {
1092 write( text );
1093 }
1094
1095
1096 public void comment( String comment )
1097 {
1098 rawText( ( startFlag ? "" : EOL ) + COMMENT + COMMENT + comment );
1099 }
1100
1101
1102
1103
1104
1105
1106
1107 public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes )
1108 {
1109 LOGGER.warn( "Unknown Sink event '{}', ignoring!", name );
1110 }
1111
1112
1113
1114
1115
1116
1117 protected void write( String text )
1118 {
1119 startFlag = false;
1120 if ( tableCellFlag )
1121 {
1122 buffer.append( text );
1123 }
1124 else
1125 {
1126 writer.write( unifyEOLs( text ) );
1127 }
1128 }
1129
1130
1131
1132
1133
1134
1135 protected void content( String text )
1136 {
1137 write( escapeAPT( text ) );
1138 }
1139
1140
1141
1142
1143
1144
1145 protected void verbatimContent( String text )
1146 {
1147 write( escapeAPT( text ) );
1148 }
1149
1150
1151
1152
1153 public void flush()
1154 {
1155 writer.flush();
1156 }
1157
1158
1159
1160
1161 public void close()
1162 {
1163 writer.close();
1164
1165 init();
1166 }
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182 private static String escapeAPT( String text )
1183 {
1184 if ( text == null )
1185 {
1186 return "";
1187 }
1188
1189 int length = text.length();
1190 StringBuilder buffer = new StringBuilder( length );
1191
1192 for ( int i = 0; i < length; ++i )
1193 {
1194 char c = text.charAt( i );
1195 switch ( c )
1196 {
1197 case '\\':
1198 case '~':
1199 case '=':
1200 case '-':
1201 case '+':
1202 case '*':
1203 case '[':
1204 case ']':
1205 case '<':
1206 case '>':
1207 case '{':
1208 case '}':
1209 buffer.append( '\\' );
1210 buffer.append( c );
1211 break;
1212 default:
1213 buffer.append( c );
1214 }
1215 }
1216
1217 return buffer.toString();
1218 }
1219 }