1 package org.apache.maven.doxia.sink.impl;
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.StringWriter;
24 import java.io.Writer;
25 import java.util.ArrayList;
26 import java.util.Enumeration;
27 import java.util.HashMap;
28 import java.util.LinkedList;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32 import java.util.Stack;
33 import java.util.TreeSet;
34
35 import javax.swing.text.MutableAttributeSet;
36 import javax.swing.text.html.HTML.Attribute;
37 import javax.swing.text.html.HTML.Tag;
38
39 import org.apache.maven.doxia.markup.HtmlMarkup;
40 import org.apache.maven.doxia.markup.Markup;
41 import org.apache.maven.doxia.sink.SinkEventAttributes;
42 import org.apache.maven.doxia.util.DoxiaUtils;
43 import org.apache.maven.doxia.util.HtmlTools;
44
45 import org.codehaus.plexus.util.StringUtils;
46 import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
47
48
49
50
51
52
53
54
55
56 public class XhtmlBaseSink
57 extends AbstractXmlSink
58 implements HtmlMarkup
59 {
60
61
62
63
64
65 private final PrintWriter writer;
66
67
68 private StringBuffer textBuffer = new StringBuffer();
69
70
71 private boolean headFlag;
72
73
74 private boolean figureCaptionFlag;
75
76
77 private boolean paragraphFlag;
78
79
80 private boolean verbatimFlag;
81
82
83 private final LinkedList<int[]> cellJustifStack;
84
85
86 private final LinkedList<Boolean> isCellJustifStack;
87
88
89 private final LinkedList<Integer> cellCountStack;
90
91
92 private boolean evenTableRow = true;
93
94
95 private final LinkedList<StringWriter> tableContentWriterStack;
96
97 private final LinkedList<StringWriter> tableCaptionWriterStack;
98
99 private final LinkedList<PrettyPrintXMLWriter> tableCaptionXMLWriterStack;
100
101
102 private final LinkedList<String> tableCaptionStack;
103
104
105 protected MutableAttributeSet tableAttributes;
106
107
108 private boolean legacyFigure;
109
110
111 private boolean legacyFigureCaption;
112
113
114 private boolean inFigure;
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129 protected boolean tableRows = false;
130
131
132 protected Stack<List<Tag>> inlineStack = new Stack<>();
133
134
135
136 private Map<String, Set<String>> warnMessages;
137
138
139
140
141
142
143
144
145
146
147 public XhtmlBaseSink( Writer out )
148 {
149 this.writer = new PrintWriter( out );
150
151 this.cellJustifStack = new LinkedList<>();
152 this.isCellJustifStack = new LinkedList<>();
153 this.cellCountStack = new LinkedList<>();
154 this.tableContentWriterStack = new LinkedList<>();
155 this.tableCaptionWriterStack = new LinkedList<>();
156 this.tableCaptionXMLWriterStack = new LinkedList<>();
157 this.tableCaptionStack = new LinkedList<>();
158
159 init();
160 }
161
162
163
164
165
166
167
168
169
170
171 protected StringBuffer getTextBuffer()
172 {
173 return this.textBuffer;
174 }
175
176
177
178
179
180
181 protected void setHeadFlag( boolean headFlag )
182 {
183 this.headFlag = headFlag;
184 }
185
186
187
188
189
190
191 protected boolean isHeadFlag()
192 {
193 return this.headFlag ;
194 }
195
196
197
198
199
200
201 protected void setVerbatimFlag( boolean verb )
202 {
203 this.verbatimFlag = verb;
204 }
205
206
207
208
209
210
211 protected boolean isVerbatimFlag()
212 {
213 return this.verbatimFlag ;
214 }
215
216
217
218
219
220
221 protected void setCellJustif( int[] justif )
222 {
223 this.cellJustifStack.addLast( justif );
224 this.isCellJustifStack.addLast( Boolean.TRUE );
225 }
226
227
228
229
230
231
232 protected int[] getCellJustif()
233 {
234 return this.cellJustifStack.getLast();
235 }
236
237
238
239
240
241
242 protected void setCellCount( int count )
243 {
244 this.cellCountStack.addLast( count );
245 }
246
247
248
249
250
251
252 protected int getCellCount()
253 {
254 return Integer.parseInt( this.cellCountStack.getLast().toString() );
255 }
256
257
258
259
260
261
262 protected void resetState()
263 {
264 init();
265 }
266
267
268 @Override
269 protected void init()
270 {
271 super.init();
272
273 resetTextBuffer();
274
275 this.cellJustifStack.clear();
276 this.isCellJustifStack.clear();
277 this.cellCountStack.clear();
278 this.tableContentWriterStack.clear();
279 this.tableCaptionWriterStack.clear();
280 this.tableCaptionXMLWriterStack.clear();
281 this.tableCaptionStack.clear();
282
283 this.headFlag = false;
284 this.figureCaptionFlag = false;
285 this.paragraphFlag = false;
286 this.verbatimFlag = false;
287
288 this.evenTableRow = true;
289 this.tableAttributes = null;
290 this.legacyFigure = false;
291 this.legacyFigureCaption = false;
292 this.inFigure = false;
293 this.tableRows = false;
294 this.warnMessages = null;
295 }
296
297
298
299
300 protected void resetTextBuffer()
301 {
302 this.textBuffer = new StringBuffer();
303 }
304
305
306
307
308
309
310 @Override
311 public void section( int level, SinkEventAttributes attributes )
312 {
313 onSection( level, attributes );
314 }
315
316
317 @Override
318 public void sectionTitle( int level, SinkEventAttributes attributes )
319 {
320 onSectionTitle( level, attributes );
321 }
322
323
324 @Override
325 public void sectionTitle_( int level )
326 {
327 onSectionTitle_( level );
328 }
329
330
331 @Override
332 public void section_( int level )
333 {
334 onSection_( level );
335 }
336
337
338 @Override
339 public void section1()
340 {
341 onSection( SECTION_LEVEL_1, null );
342 }
343
344
345 @Override
346 public void sectionTitle1()
347 {
348 onSectionTitle( SECTION_LEVEL_1, null );
349 }
350
351
352 @Override
353 public void sectionTitle1_()
354 {
355 onSectionTitle_( SECTION_LEVEL_1 );
356 }
357
358
359 @Override
360 public void section1_()
361 {
362 onSection_( SECTION_LEVEL_1 );
363 }
364
365
366 @Override
367 public void section2()
368 {
369 onSection( SECTION_LEVEL_2, null );
370 }
371
372
373 @Override
374 public void sectionTitle2()
375 {
376 onSectionTitle( SECTION_LEVEL_2, null );
377 }
378
379
380 @Override
381 public void sectionTitle2_()
382 {
383 onSectionTitle_( SECTION_LEVEL_2 );
384 }
385
386
387 @Override
388 public void section2_()
389 {
390 onSection_( SECTION_LEVEL_2 );
391 }
392
393
394 @Override
395 public void section3()
396 {
397 onSection( SECTION_LEVEL_3, null );
398 }
399
400
401 @Override
402 public void sectionTitle3()
403 {
404 onSectionTitle( SECTION_LEVEL_3, null );
405 }
406
407
408 @Override
409 public void sectionTitle3_()
410 {
411 onSectionTitle_( SECTION_LEVEL_3 );
412 }
413
414
415 @Override
416 public void section3_()
417 {
418 onSection_( SECTION_LEVEL_3 );
419 }
420
421
422 @Override
423 public void section4()
424 {
425 onSection( SECTION_LEVEL_4, null );
426 }
427
428
429 @Override
430 public void sectionTitle4()
431 {
432 onSectionTitle( SECTION_LEVEL_4, null );
433 }
434
435
436 @Override
437 public void sectionTitle4_()
438 {
439 onSectionTitle_( SECTION_LEVEL_4 );
440 }
441
442
443 @Override
444 public void section4_()
445 {
446 onSection_( SECTION_LEVEL_4 );
447 }
448
449
450 @Override
451 public void section5()
452 {
453 onSection( SECTION_LEVEL_5, null );
454 }
455
456
457 @Override
458 public void sectionTitle5()
459 {
460 onSectionTitle( SECTION_LEVEL_5, null );
461 }
462
463
464 @Override
465 public void sectionTitle5_()
466 {
467 onSectionTitle_( SECTION_LEVEL_5 );
468 }
469
470
471 @Override
472 public void section5_()
473 {
474 onSection_( SECTION_LEVEL_5 );
475 }
476
477
478
479
480
481
482
483
484 protected void onSection( int depth, SinkEventAttributes attributes )
485 {
486 if ( depth >= SECTION_LEVEL_1 && depth <= SECTION_LEVEL_5 )
487 {
488 MutableAttributeSet att = new SinkEventAttributeSet();
489 att.addAttribute( Attribute.CLASS, "section" );
490
491 att.addAttributes( SinkUtils.filterAttributes(
492 attributes, SinkUtils.SINK_BASE_ATTRIBUTES ) );
493
494 writeStartTag( HtmlMarkup.DIV, att );
495 }
496 }
497
498
499
500
501
502
503
504 protected void onSection_( int depth )
505 {
506 if ( depth >= SECTION_LEVEL_1 && depth <= SECTION_LEVEL_5 )
507 {
508 writeEndTag( HtmlMarkup.DIV );
509 }
510 }
511
512
513
514
515
516
517
518
519
520
521
522
523 protected void onSectionTitle( int depth, SinkEventAttributes attributes )
524 {
525 MutableAttributeSet atts = SinkUtils.filterAttributes(
526 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES );
527
528 if ( depth == SECTION_LEVEL_1 )
529 {
530 writeStartTag( HtmlMarkup.H2, atts );
531 }
532 else if ( depth == SECTION_LEVEL_2 )
533 {
534 writeStartTag( HtmlMarkup.H3, atts );
535 }
536 else if ( depth == SECTION_LEVEL_3 )
537 {
538 writeStartTag( HtmlMarkup.H4, atts );
539 }
540 else if ( depth == SECTION_LEVEL_4 )
541 {
542 writeStartTag( HtmlMarkup.H5, atts );
543 }
544 else if ( depth == SECTION_LEVEL_5 )
545 {
546 writeStartTag( HtmlMarkup.H6, atts );
547 }
548 }
549
550
551
552
553
554
555
556
557
558
559
560 protected void onSectionTitle_( int depth )
561 {
562 if ( depth == SECTION_LEVEL_1 )
563 {
564 writeEndTag( HtmlMarkup.H2 );
565 }
566 else if ( depth == SECTION_LEVEL_2 )
567 {
568 writeEndTag( HtmlMarkup.H3 );
569 }
570 else if ( depth == SECTION_LEVEL_3 )
571 {
572 writeEndTag( HtmlMarkup.H4 );
573 }
574 else if ( depth == SECTION_LEVEL_4 )
575 {
576 writeEndTag( HtmlMarkup.H5 );
577 }
578 else if ( depth == SECTION_LEVEL_5 )
579 {
580 writeEndTag( HtmlMarkup.H6 );
581 }
582 }
583
584
585
586
587
588
589
590
591
592 @Override
593 public void list()
594 {
595 list( null );
596 }
597
598
599
600
601
602 @Override
603 public void list( SinkEventAttributes attributes )
604 {
605 if ( paragraphFlag )
606 {
607
608
609
610 paragraph_();
611 }
612
613 MutableAttributeSet atts = SinkUtils.filterAttributes(
614 attributes, SinkUtils.SINK_BASE_ATTRIBUTES );
615
616 writeStartTag( HtmlMarkup.UL, atts );
617 }
618
619
620
621
622
623 @Override
624 public void list_()
625 {
626 writeEndTag( HtmlMarkup.UL );
627 }
628
629
630
631
632
633 @Override
634 public void listItem()
635 {
636 listItem( null );
637 }
638
639
640
641
642
643 @Override
644 public void listItem( SinkEventAttributes attributes )
645 {
646 MutableAttributeSet atts = SinkUtils.filterAttributes(
647 attributes, SinkUtils.SINK_BASE_ATTRIBUTES );
648
649 writeStartTag( HtmlMarkup.LI, atts );
650 }
651
652
653
654
655
656 @Override
657 public void listItem_()
658 {
659 writeEndTag( HtmlMarkup.LI );
660 }
661
662
663
664
665
666
667
668 @Override
669 public void numberedList( int numbering )
670 {
671 numberedList( numbering, null );
672 }
673
674
675
676
677
678
679
680 @Override
681 public void numberedList( int numbering, SinkEventAttributes attributes )
682 {
683 if ( paragraphFlag )
684 {
685
686
687
688 paragraph_();
689 }
690
691 String style;
692 switch ( numbering )
693 {
694 case NUMBERING_UPPER_ALPHA:
695 style = "upper-alpha";
696 break;
697 case NUMBERING_LOWER_ALPHA:
698 style = "lower-alpha";
699 break;
700 case NUMBERING_UPPER_ROMAN:
701 style = "upper-roman";
702 break;
703 case NUMBERING_LOWER_ROMAN:
704 style = "lower-roman";
705 break;
706 case NUMBERING_DECIMAL:
707 default:
708 style = "decimal";
709 }
710
711 MutableAttributeSet atts = SinkUtils.filterAttributes(
712 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES );
713
714 if ( atts == null )
715 {
716 atts = new SinkEventAttributeSet( 1 );
717 }
718
719 atts.addAttribute( Attribute.STYLE, "list-style-type: " + style );
720
721 writeStartTag( HtmlMarkup.OL, atts );
722 }
723
724
725
726
727
728 @Override
729 public void numberedList_()
730 {
731 writeEndTag( HtmlMarkup.OL );
732 }
733
734
735
736
737
738 @Override
739 public void numberedListItem()
740 {
741 numberedListItem( null );
742 }
743
744
745
746
747
748 @Override
749 public void numberedListItem( SinkEventAttributes attributes )
750 {
751 MutableAttributeSet atts = SinkUtils.filterAttributes(
752 attributes, SinkUtils.SINK_BASE_ATTRIBUTES );
753
754 writeStartTag( HtmlMarkup.LI, atts );
755 }
756
757
758
759
760
761 @Override
762 public void numberedListItem_()
763 {
764 writeEndTag( HtmlMarkup.LI );
765 }
766
767
768
769
770
771 @Override
772 public void definitionList()
773 {
774 definitionList( null );
775 }
776
777
778
779
780
781 @Override
782 public void definitionList( SinkEventAttributes attributes )
783 {
784 if ( paragraphFlag )
785 {
786
787
788
789 paragraph_();
790 }
791
792 MutableAttributeSet atts = SinkUtils.filterAttributes(
793 attributes, SinkUtils.SINK_BASE_ATTRIBUTES );
794
795 writeStartTag( HtmlMarkup.DL, atts );
796 }
797
798
799
800
801
802 @Override
803 public void definitionList_()
804 {
805 writeEndTag( HtmlMarkup.DL );
806 }
807
808
809
810
811
812 @Override
813 public void definedTerm( SinkEventAttributes attributes )
814 {
815 MutableAttributeSet atts = SinkUtils.filterAttributes(
816 attributes, SinkUtils.SINK_BASE_ATTRIBUTES );
817
818 writeStartTag( HtmlMarkup.DT, atts );
819 }
820
821
822
823
824
825 @Override
826 public void definedTerm()
827 {
828 definedTerm( null );
829 }
830
831
832
833
834
835 @Override
836 public void definedTerm_()
837 {
838 writeEndTag( HtmlMarkup.DT );
839 }
840
841
842
843
844
845 @Override
846 public void definition()
847 {
848 definition( null );
849 }
850
851
852
853
854
855 @Override
856 public void definition( SinkEventAttributes attributes )
857 {
858 MutableAttributeSet atts = SinkUtils.filterAttributes(
859 attributes, SinkUtils.SINK_BASE_ATTRIBUTES );
860
861 writeStartTag( HtmlMarkup.DD, atts );
862 }
863
864
865
866
867
868 @Override
869 public void definition_()
870 {
871 writeEndTag( HtmlMarkup.DD );
872 }
873
874
875
876
877
878
879
880
881 @Override
882 public void figure()
883 {
884 write( String.valueOf( LESS_THAN ) + HtmlMarkup.IMG );
885 legacyFigure = true;
886 }
887
888
889
890
891
892 @Override
893 public void figure( SinkEventAttributes attributes )
894 {
895 inFigure = true;
896
897 MutableAttributeSet atts = SinkUtils.filterAttributes(
898 attributes, SinkUtils.SINK_BASE_ATTRIBUTES );
899
900 if ( atts == null )
901 {
902 atts = new SinkEventAttributeSet( 1 );
903 }
904
905 if ( !atts.isDefined( SinkEventAttributes.CLASS ) )
906 {
907 atts.addAttribute( SinkEventAttributes.CLASS, "figure" );
908 }
909
910 writeStartTag( HtmlMarkup.DIV, atts );
911 }
912
913
914 @Override
915 public void figure_()
916 {
917 if ( legacyFigure )
918 {
919 if ( !figureCaptionFlag )
920 {
921
922 write( String.valueOf( SPACE ) + Attribute.ALT + EQUAL + QUOTE + QUOTE );
923 }
924 write( String.valueOf( SPACE ) + SLASH + GREATER_THAN );
925 legacyFigure = false;
926 }
927 else
928 {
929 writeEndTag( HtmlMarkup.DIV );
930 inFigure = false;
931 }
932
933 figureCaptionFlag = false;
934 }
935
936
937
938
939
940
941
942 @Override
943 public void figureGraphics( String name )
944 {
945 write( String.valueOf( SPACE ) + Attribute.SRC + EQUAL + QUOTE + escapeHTML( name ) + QUOTE );
946 }
947
948
949 @Override
950 public void figureGraphics( String src, SinkEventAttributes attributes )
951 {
952 if ( inFigure )
953 {
954 MutableAttributeSet atts = new SinkEventAttributeSet( 1 );
955 atts.addAttribute( SinkEventAttributes.ALIGN, "center" );
956
957 writeStartTag( HtmlMarkup.P, atts );
958 }
959
960 MutableAttributeSet filtered = SinkUtils.filterAttributes( attributes, SinkUtils.SINK_IMG_ATTRIBUTES );
961 if ( filtered != null )
962 {
963 filtered.removeAttribute( Attribute.SRC.toString() );
964 }
965
966 int count = ( attributes == null ? 1 : attributes.getAttributeCount() + 1 );
967
968 MutableAttributeSet atts = new SinkEventAttributeSet( count );
969
970 atts.addAttribute( Attribute.SRC, HtmlTools.escapeHTML( src, true ) );
971 atts.addAttributes( filtered );
972
973 if ( atts.getAttribute( Attribute.ALT.toString() ) == null )
974 {
975 atts.addAttribute( Attribute.ALT.toString(), "" );
976 }
977
978 writeStartTag( HtmlMarkup.IMG, atts, true );
979
980 if ( inFigure )
981 {
982 writeEndTag( HtmlMarkup.P );
983 }
984 }
985
986
987
988
989
990
991
992 @Override
993 public void figureCaption()
994 {
995 figureCaptionFlag = true;
996 write( String.valueOf( SPACE ) + Attribute.ALT + EQUAL + QUOTE );
997 legacyFigureCaption = true;
998 }
999
1000
1001 @Override
1002 public void figureCaption( SinkEventAttributes attributes )
1003 {
1004 if ( legacyFigureCaption )
1005 {
1006 write( String.valueOf( SPACE ) + Attribute.ALT + EQUAL + QUOTE );
1007 legacyFigureCaption = false;
1008 figureCaptionFlag = true;
1009 }
1010 else
1011 {
1012 SinkEventAttributeSet atts = new SinkEventAttributeSet( 1 );
1013 atts.addAttribute( SinkEventAttributes.ALIGN, "center" );
1014 atts.addAttributes( SinkUtils.filterAttributes(
1015 attributes, SinkUtils.SINK_BASE_ATTRIBUTES ) );
1016
1017 paragraph( atts );
1018 inline( SinkEventAttributeSet.Semantics.ITALIC );
1019 }
1020 }
1021
1022
1023 @Override
1024 public void figureCaption_()
1025 {
1026 if ( legacyFigureCaption )
1027 {
1028 write( String.valueOf( QUOTE ) );
1029 }
1030 else
1031 {
1032 inline_();
1033 paragraph_();
1034 }
1035 }
1036
1037
1038
1039
1040
1041 @Override
1042 public void paragraph()
1043 {
1044 paragraph( null );
1045 }
1046
1047
1048
1049
1050
1051 @Override
1052 public void paragraph( SinkEventAttributes attributes )
1053 {
1054 paragraphFlag = true;
1055
1056 MutableAttributeSet atts = SinkUtils.filterAttributes(
1057 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES );
1058
1059 writeStartTag( HtmlMarkup.P, atts );
1060 }
1061
1062
1063
1064
1065
1066 @Override
1067 public void paragraph_()
1068 {
1069 if ( paragraphFlag )
1070 {
1071 writeEndTag( HtmlMarkup.P );
1072 paragraphFlag = false;
1073 }
1074 }
1075
1076
1077
1078
1079
1080 @Override
1081 public void address()
1082 {
1083 address( null );
1084 }
1085
1086
1087
1088
1089
1090 @Override
1091 public void address( SinkEventAttributes attributes )
1092 {
1093 MutableAttributeSet atts = SinkUtils.filterAttributes(
1094 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES );
1095
1096 writeStartTag( HtmlMarkup.ADDRESS, atts );
1097 }
1098
1099
1100
1101
1102
1103 @Override
1104 public void address_()
1105 {
1106 writeEndTag( HtmlMarkup.ADDRESS );
1107 }
1108
1109
1110
1111
1112
1113 @Override
1114 public void blockquote()
1115 {
1116 blockquote( null );
1117 }
1118
1119
1120
1121
1122
1123 @Override
1124 public void blockquote( SinkEventAttributes attributes )
1125 {
1126 MutableAttributeSet atts = SinkUtils.filterAttributes(
1127 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES );
1128
1129 writeStartTag( HtmlMarkup.BLOCKQUOTE, atts );
1130 }
1131
1132
1133
1134
1135
1136 @Override
1137 public void blockquote_()
1138 {
1139 writeEndTag( HtmlMarkup.BLOCKQUOTE );
1140 }
1141
1142
1143
1144
1145
1146 @Override
1147 public void division()
1148 {
1149 division( null );
1150 }
1151
1152
1153
1154
1155
1156 @Override
1157 public void division( SinkEventAttributes attributes )
1158 {
1159 MutableAttributeSet atts = SinkUtils.filterAttributes(
1160 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES );
1161
1162 writeStartTag( HtmlMarkup.DIV, atts );
1163 }
1164
1165
1166
1167
1168
1169 @Override
1170 public void division_()
1171 {
1172 writeEndTag( HtmlMarkup.DIV );
1173 }
1174
1175
1176
1177
1178
1179
1180
1181
1182 @Override
1183 public void verbatim( boolean boxed )
1184 {
1185 if ( boxed )
1186 {
1187 verbatim( SinkEventAttributeSet.BOXED );
1188 }
1189 else
1190 {
1191 verbatim( null );
1192 }
1193 }
1194
1195
1196
1197
1198
1199
1200
1201
1202 @Override
1203 public void verbatim( SinkEventAttributes attributes )
1204 {
1205 if ( paragraphFlag )
1206 {
1207
1208
1209
1210 paragraph_();
1211 }
1212
1213 verbatimFlag = true;
1214
1215 MutableAttributeSet atts = SinkUtils.filterAttributes(
1216 attributes, SinkUtils.SINK_VERBATIM_ATTRIBUTES );
1217
1218 if ( atts == null )
1219 {
1220 atts = new SinkEventAttributeSet();
1221 }
1222
1223 boolean boxed = false;
1224
1225 if ( atts.isDefined( SinkEventAttributes.DECORATION ) )
1226 {
1227 boxed =
1228 "boxed".equals( atts.getAttribute( SinkEventAttributes.DECORATION ).toString() );
1229 }
1230
1231 SinkEventAttributes divAtts = null;
1232
1233 if ( boxed )
1234 {
1235 divAtts = new SinkEventAttributeSet( Attribute.CLASS.toString(), "source" );
1236 }
1237
1238 atts.removeAttribute( SinkEventAttributes.DECORATION );
1239
1240 writeStartTag( HtmlMarkup.DIV, divAtts );
1241 writeStartTag( HtmlMarkup.PRE, atts );
1242 }
1243
1244
1245
1246
1247
1248
1249 @Override
1250 public void verbatim_()
1251 {
1252 writeEndTag( HtmlMarkup.PRE );
1253 writeEndTag( HtmlMarkup.DIV );
1254
1255 verbatimFlag = false;
1256
1257 }
1258
1259
1260
1261
1262
1263 @Override
1264 public void horizontalRule()
1265 {
1266 horizontalRule( null );
1267 }
1268
1269
1270
1271
1272
1273 @Override
1274 public void horizontalRule( SinkEventAttributes attributes )
1275 {
1276 MutableAttributeSet atts = SinkUtils.filterAttributes(
1277 attributes, SinkUtils.SINK_HR_ATTRIBUTES );
1278
1279 writeSimpleTag( HtmlMarkup.HR, atts );
1280 }
1281
1282
1283 @Override
1284 public void table()
1285 {
1286
1287 table( null );
1288 }
1289
1290
1291 @Override
1292 public void table( SinkEventAttributes attributes )
1293 {
1294 this.tableContentWriterStack.addLast( new StringWriter() );
1295 this.tableRows = false;
1296
1297 if ( paragraphFlag )
1298 {
1299
1300
1301
1302 paragraph_();
1303 }
1304
1305
1306 if ( attributes == null )
1307 {
1308 this.tableAttributes = new SinkEventAttributeSet( 0 );
1309 }
1310 else
1311 {
1312 this.tableAttributes = SinkUtils.filterAttributes(
1313 attributes, SinkUtils.SINK_TABLE_ATTRIBUTES );
1314 }
1315 }
1316
1317
1318
1319
1320
1321 @Override
1322 public void table_()
1323 {
1324 this.tableRows = false;
1325
1326 writeEndTag( HtmlMarkup.TABLE );
1327
1328 if ( !this.cellCountStack.isEmpty() )
1329 {
1330 this.cellCountStack.removeLast().toString();
1331 }
1332
1333 if ( this.tableContentWriterStack.isEmpty() )
1334 {
1335 if ( getLog().isWarnEnabled() )
1336 {
1337 getLog().warn( "No table content." );
1338 }
1339 return;
1340 }
1341
1342 String tableContent = this.tableContentWriterStack.removeLast().toString();
1343
1344 String tableCaption = null;
1345 if ( !this.tableCaptionStack.isEmpty() && this.tableCaptionStack.getLast() != null )
1346 {
1347 tableCaption = this.tableCaptionStack.removeLast();
1348 }
1349
1350 if ( tableCaption != null )
1351 {
1352
1353 StringBuilder sb = new StringBuilder();
1354 sb.append( tableContent, 0, tableContent.indexOf( Markup.GREATER_THAN ) + 1 );
1355 sb.append( tableCaption );
1356 sb.append( tableContent.substring( tableContent.indexOf( Markup.GREATER_THAN ) + 1 ) );
1357
1358 write( sb.toString() );
1359 }
1360 else
1361 {
1362 write( tableContent );
1363 }
1364 }
1365
1366
1367
1368
1369
1370
1371
1372
1373 @Override
1374 public void tableRows( int[] justification, boolean grid )
1375 {
1376 this.tableRows = true;
1377
1378 setCellJustif( justification );
1379
1380 if ( this.tableAttributes == null )
1381 {
1382 this.tableAttributes = new SinkEventAttributeSet( 0 );
1383 }
1384
1385 MutableAttributeSet att = new SinkEventAttributeSet();
1386 if ( !this.tableAttributes.isDefined( Attribute.BORDER.toString() ) )
1387 {
1388 att.addAttribute( Attribute.BORDER, ( grid ? "1" : "0" ) );
1389 }
1390
1391 if ( !this.tableAttributes.isDefined( Attribute.CLASS.toString() ) )
1392 {
1393 att.addAttribute( Attribute.CLASS, "bodyTable" );
1394 }
1395
1396 att.addAttributes( this.tableAttributes );
1397 this.tableAttributes.removeAttributes( this.tableAttributes );
1398
1399 writeStartTag( HtmlMarkup.TABLE, att );
1400
1401 this.cellCountStack.addLast( 0 );
1402 }
1403
1404
1405 @Override
1406 public void tableRows_()
1407 {
1408 this.tableRows = false;
1409 if ( !this.cellJustifStack.isEmpty() )
1410 {
1411 this.cellJustifStack.removeLast();
1412 }
1413 if ( !this.isCellJustifStack.isEmpty() )
1414 {
1415 this.isCellJustifStack.removeLast();
1416 }
1417
1418 this.evenTableRow = true;
1419 }
1420
1421
1422
1423
1424
1425
1426
1427 @Override
1428 public void tableRow()
1429 {
1430
1431 if ( !this.tableRows )
1432 {
1433 tableRows( null, false );
1434 }
1435 tableRow( null );
1436 }
1437
1438
1439
1440
1441
1442
1443
1444 @Override
1445 public void tableRow( SinkEventAttributes attributes )
1446 {
1447 MutableAttributeSet att = new SinkEventAttributeSet();
1448
1449 if ( evenTableRow )
1450 {
1451 att.addAttribute( Attribute.CLASS, "a" );
1452 }
1453 else
1454 {
1455 att.addAttribute( Attribute.CLASS, "b" );
1456 }
1457
1458 att.addAttributes( SinkUtils.filterAttributes(
1459 attributes, SinkUtils.SINK_TR_ATTRIBUTES ) );
1460
1461 writeStartTag( HtmlMarkup.TR, att );
1462
1463 evenTableRow = !evenTableRow;
1464
1465 if ( !this.cellCountStack.isEmpty() )
1466 {
1467 this.cellCountStack.removeLast();
1468 this.cellCountStack.addLast( 0 );
1469 }
1470 }
1471
1472
1473
1474
1475
1476 @Override
1477 public void tableRow_()
1478 {
1479 writeEndTag( HtmlMarkup.TR );
1480 }
1481
1482
1483 @Override
1484 public void tableCell()
1485 {
1486 tableCell( (SinkEventAttributeSet) null );
1487 }
1488
1489
1490 @Override
1491 public void tableHeaderCell()
1492 {
1493 tableHeaderCell( (SinkEventAttributeSet) null );
1494 }
1495
1496
1497 @Override
1498 public void tableCell( String width )
1499 {
1500 MutableAttributeSet att = new SinkEventAttributeSet();
1501 att.addAttribute( Attribute.WIDTH, width );
1502
1503 tableCell( false, att );
1504 }
1505
1506
1507 @Override
1508 public void tableHeaderCell( String width )
1509 {
1510 MutableAttributeSet att = new SinkEventAttributeSet();
1511 att.addAttribute( Attribute.WIDTH, width );
1512
1513 tableCell( true, att );
1514 }
1515
1516
1517 @Override
1518 public void tableCell( SinkEventAttributes attributes )
1519 {
1520 tableCell( false, attributes );
1521 }
1522
1523
1524 @Override
1525 public void tableHeaderCell( SinkEventAttributes attributes )
1526 {
1527 tableCell( true, attributes );
1528 }
1529
1530
1531
1532
1533
1534
1535
1536 private void tableCell( boolean headerRow, MutableAttributeSet attributes )
1537 {
1538 Tag t = ( headerRow ? HtmlMarkup.TH : HtmlMarkup.TD );
1539
1540 if ( attributes == null )
1541 {
1542 writeStartTag( t, null );
1543 }
1544 else
1545 {
1546 writeStartTag( t,
1547 SinkUtils.filterAttributes( attributes, SinkUtils.SINK_TD_ATTRIBUTES ) );
1548 }
1549 }
1550
1551
1552 @Override
1553 public void tableCell_()
1554 {
1555 tableCell_( false );
1556 }
1557
1558
1559 @Override
1560 public void tableHeaderCell_()
1561 {
1562 tableCell_( true );
1563 }
1564
1565
1566
1567
1568
1569
1570
1571
1572 private void tableCell_( boolean headerRow )
1573 {
1574 Tag t = ( headerRow ? HtmlMarkup.TH : HtmlMarkup.TD );
1575
1576 writeEndTag( t );
1577
1578 if ( !this.isCellJustifStack.isEmpty() && this.isCellJustifStack.getLast().equals( Boolean.TRUE )
1579 && !this.cellCountStack.isEmpty() )
1580 {
1581 int cellCount = Integer.parseInt( this.cellCountStack.removeLast().toString() );
1582 this.cellCountStack.addLast( ++cellCount );
1583 }
1584 }
1585
1586
1587
1588
1589
1590 @Override
1591 public void tableCaption()
1592 {
1593 tableCaption( null );
1594 }
1595
1596
1597
1598
1599
1600 @Override
1601 public void tableCaption( SinkEventAttributes attributes )
1602 {
1603 StringWriter sw = new StringWriter();
1604 this.tableCaptionWriterStack.addLast( sw );
1605 this.tableCaptionXMLWriterStack.addLast( new PrettyPrintXMLWriter( sw ) );
1606
1607
1608 MutableAttributeSet atts = SinkUtils.filterAttributes(
1609 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES );
1610
1611 writeStartTag( HtmlMarkup.CAPTION, atts );
1612 }
1613
1614
1615
1616
1617
1618 @Override
1619 public void tableCaption_()
1620 {
1621 writeEndTag( HtmlMarkup.CAPTION );
1622
1623 if ( !this.tableCaptionXMLWriterStack.isEmpty() && this.tableCaptionXMLWriterStack.getLast() != null )
1624 {
1625 this.tableCaptionStack.addLast( this.tableCaptionWriterStack.removeLast().toString() );
1626 this.tableCaptionXMLWriterStack.removeLast();
1627 }
1628 }
1629
1630
1631
1632
1633
1634 @Override
1635 public void anchor( String name )
1636 {
1637 anchor( name, null );
1638 }
1639
1640
1641
1642
1643
1644 @Override
1645 public void anchor( String name, SinkEventAttributes attributes )
1646 {
1647 if ( name == null )
1648 {
1649 throw new NullPointerException( "Anchor name cannot be null!" );
1650 }
1651
1652 if ( headFlag )
1653 {
1654 return;
1655 }
1656
1657 MutableAttributeSet atts = SinkUtils.filterAttributes(
1658 attributes, SinkUtils.SINK_BASE_ATTRIBUTES );
1659
1660 String id = name;
1661
1662 if ( !DoxiaUtils.isValidId( id ) )
1663 {
1664 id = DoxiaUtils.encodeId( name, true );
1665
1666 String msg = "Modified invalid anchor name: '" + name + "' to '" + id + "'";
1667 logMessage( "modifiedLink", msg );
1668 }
1669
1670 MutableAttributeSet att = new SinkEventAttributeSet();
1671 att.addAttribute( Attribute.NAME, id );
1672 att.addAttributes( atts );
1673
1674 writeStartTag( HtmlMarkup.A, att );
1675 }
1676
1677
1678
1679
1680
1681 @Override
1682 public void anchor_()
1683 {
1684 if ( !headFlag )
1685 {
1686 writeEndTag( HtmlMarkup.A );
1687 }
1688 }
1689
1690
1691 @Override
1692 public void link( String name )
1693 {
1694 link( name, null );
1695 }
1696
1697
1698 @Override
1699 public void link( String name, SinkEventAttributes attributes )
1700 {
1701 if ( attributes == null )
1702 {
1703 link( name, null, null );
1704 }
1705 else
1706 {
1707 String target = (String) attributes.getAttribute( Attribute.TARGET.toString() );
1708 MutableAttributeSet atts = SinkUtils.filterAttributes(
1709 attributes, SinkUtils.SINK_LINK_ATTRIBUTES );
1710
1711 link( name, target, atts );
1712 }
1713 }
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725 private void link( String href, String target, MutableAttributeSet attributes )
1726 {
1727 if ( href == null )
1728 {
1729 throw new NullPointerException( "Link name cannot be null!" );
1730 }
1731
1732 if ( headFlag )
1733 {
1734 return;
1735 }
1736
1737 MutableAttributeSet att = new SinkEventAttributeSet();
1738
1739 if ( DoxiaUtils.isExternalLink( href ) )
1740 {
1741 att.addAttribute( Attribute.CLASS, "externalLink" );
1742 }
1743
1744 att.addAttribute( Attribute.HREF, HtmlTools.escapeHTML( href ) );
1745
1746 if ( target != null )
1747 {
1748 att.addAttribute( Attribute.TARGET, target );
1749 }
1750
1751 if ( attributes != null )
1752 {
1753 attributes.removeAttribute( Attribute.HREF.toString() );
1754 attributes.removeAttribute( Attribute.TARGET.toString() );
1755 att.addAttributes( attributes );
1756 }
1757
1758 writeStartTag( HtmlMarkup.A, att );
1759 }
1760
1761
1762
1763
1764
1765 @Override
1766 public void link_()
1767 {
1768 if ( !headFlag )
1769 {
1770 writeEndTag( HtmlMarkup.A );
1771 }
1772 }
1773
1774
1775 @Override
1776 public void inline()
1777 {
1778 inline( null );
1779 }
1780
1781 private void inlineSemantics( SinkEventAttributes attributes, String semantic,
1782 List<Tag> tags, Tag tag )
1783 {
1784 if ( attributes.containsAttribute( SinkEventAttributes.SEMANTICS, semantic ) )
1785 {
1786 writeStartTag( tag );
1787 tags.add( 0, tag );
1788 }
1789 }
1790
1791
1792 @Override
1793 public void inline( SinkEventAttributes attributes )
1794 {
1795 if ( !headFlag )
1796 {
1797 List<Tag> tags = new ArrayList<>();
1798
1799 if ( attributes != null )
1800 {
1801 inlineSemantics( attributes, "emphasis", tags, HtmlMarkup.EM );
1802 inlineSemantics( attributes, "strong", tags, HtmlMarkup.STRONG );
1803 inlineSemantics( attributes, "small", tags, HtmlMarkup.SMALL );
1804 inlineSemantics( attributes, "line-through", tags, HtmlMarkup.S );
1805 inlineSemantics( attributes, "citation", tags, HtmlMarkup.CITE );
1806 inlineSemantics( attributes, "quote", tags, HtmlMarkup.Q );
1807 inlineSemantics( attributes, "definition", tags, HtmlMarkup.DFN );
1808 inlineSemantics( attributes, "abbreviation", tags, HtmlMarkup.ABBR );
1809 inlineSemantics( attributes, "italic", tags, HtmlMarkup.I );
1810 inlineSemantics( attributes, "bold", tags, HtmlMarkup.B );
1811 inlineSemantics( attributes, "monospaced", tags, HtmlMarkup.TT );
1812 inlineSemantics( attributes, "code", tags, HtmlMarkup.CODE );
1813 inlineSemantics( attributes, "variable", tags, HtmlMarkup.VAR );
1814 inlineSemantics( attributes, "sample", tags, HtmlMarkup.SAMP );
1815 inlineSemantics( attributes, "keyboard", tags, HtmlMarkup.KBD );
1816 inlineSemantics( attributes, "superscript", tags, HtmlMarkup.SUP );
1817 inlineSemantics( attributes, "subscript", tags, HtmlMarkup.SUB );
1818 inlineSemantics( attributes, "annotation", tags, HtmlMarkup.U );
1819 inlineSemantics( attributes, "bidirectionalOverride", tags, HtmlMarkup.BDO );
1820 inlineSemantics( attributes, "phrase", tags, HtmlMarkup.SPAN );
1821 inlineSemantics( attributes, "insert", tags, HtmlMarkup.INS );
1822 inlineSemantics( attributes, "delete", tags, HtmlMarkup.DEL );
1823 }
1824
1825 inlineStack.push( tags );
1826 }
1827 }
1828
1829
1830 @Override
1831 public void inline_()
1832 {
1833 if ( !headFlag )
1834 {
1835 for ( Tag tag: inlineStack.pop() )
1836 {
1837 writeEndTag( tag );
1838 }
1839 }
1840 }
1841
1842
1843
1844
1845
1846 @Override
1847 public void italic()
1848 {
1849 inline( SinkEventAttributeSet.Semantics.ITALIC );
1850 }
1851
1852
1853
1854
1855
1856 @Override
1857 public void italic_()
1858 {
1859 inline_();
1860 }
1861
1862
1863
1864
1865
1866 @Override
1867 public void bold()
1868 {
1869 inline( SinkEventAttributeSet.Semantics.BOLD );
1870 }
1871
1872
1873
1874
1875
1876 @Override
1877 public void bold_()
1878 {
1879 inline_();
1880 }
1881
1882
1883
1884
1885
1886 @Override
1887 public void monospaced()
1888 {
1889 inline( SinkEventAttributeSet.Semantics.MONOSPACED );
1890 }
1891
1892
1893
1894
1895
1896 @Override
1897 public void monospaced_()
1898 {
1899 inline_();
1900 }
1901
1902
1903
1904
1905
1906 @Override
1907 public void lineBreak()
1908 {
1909 lineBreak( null );
1910 }
1911
1912
1913
1914
1915
1916 @Override
1917 public void lineBreak( SinkEventAttributes attributes )
1918 {
1919 if ( headFlag || isVerbatimFlag() )
1920 {
1921 getTextBuffer().append( EOL );
1922 }
1923 else
1924 {
1925 MutableAttributeSet atts = SinkUtils.filterAttributes(
1926 attributes, SinkUtils.SINK_BR_ATTRIBUTES );
1927
1928 writeSimpleTag( HtmlMarkup.BR, atts );
1929 }
1930 }
1931
1932
1933 @Override
1934 public void pageBreak()
1935 {
1936 comment( " PB " );
1937 }
1938
1939
1940 @Override
1941 public void nonBreakingSpace()
1942 {
1943 if ( headFlag )
1944 {
1945 getTextBuffer().append( ' ' );
1946 }
1947 else
1948 {
1949 write( " " );
1950 }
1951 }
1952
1953
1954 @Override
1955 public void text( String text )
1956 {
1957 if ( headFlag )
1958 {
1959 getTextBuffer().append( text );
1960 }
1961 else if ( verbatimFlag )
1962 {
1963 verbatimContent( text );
1964 }
1965 else
1966 {
1967 content( text );
1968 }
1969 }
1970
1971
1972 @Override
1973 public void text( String text, SinkEventAttributes attributes )
1974 {
1975 text( text );
1976 }
1977
1978
1979 @Override
1980 public void rawText( String text )
1981 {
1982 if ( headFlag )
1983 {
1984 getTextBuffer().append( text );
1985 }
1986 else
1987 {
1988 write( text );
1989 }
1990 }
1991
1992
1993 @Override
1994 public void comment( String comment )
1995 {
1996 if ( comment != null )
1997 {
1998 final String originalComment = comment;
1999
2000
2001 while ( comment.contains( "--" ) )
2002 {
2003 comment = comment.replace( "--", "- -" );
2004 }
2005
2006 if ( comment.endsWith( "-" ) )
2007 {
2008 comment += " ";
2009 }
2010
2011 if ( !originalComment.equals( comment ) )
2012 {
2013 getLog().warn( "[Xhtml Sink] Modified invalid comment '" + originalComment + "' to '" + comment + "'" );
2014 }
2015
2016 final StringBuilder buffer = new StringBuilder( comment.length() + 7 );
2017
2018 buffer.append( LESS_THAN ).append( BANG ).append( MINUS ).append( MINUS );
2019 buffer.append( comment );
2020 buffer.append( MINUS ).append( MINUS ).append( GREATER_THAN );
2021
2022 write( buffer.toString() );
2023 }
2024 }
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065 @Override
2066 public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes )
2067 {
2068 if ( requiredParams == null || !( requiredParams[0] instanceof Integer ) )
2069 {
2070 String msg = "No type information for unknown event: '" + name + "', ignoring!";
2071 logMessage( "noTypeInfo", msg );
2072
2073 return;
2074 }
2075
2076 int tagType = (Integer) requiredParams[0];
2077
2078 if ( tagType == ENTITY_TYPE )
2079 {
2080 rawText( name );
2081
2082 return;
2083 }
2084
2085 if ( tagType == CDATA_TYPE )
2086 {
2087 rawText( EOL + "//<![CDATA[" + requiredParams[1] + "]]>" + EOL );
2088
2089 return;
2090 }
2091
2092 Tag tag = HtmlTools.getHtmlTag( name );
2093
2094 if ( tag == null )
2095 {
2096 String msg = "No HTML tag found for unknown event: '" + name + "', ignoring!";
2097 logMessage( "noHtmlTag", msg );
2098 }
2099 else
2100 {
2101 if ( tagType == TAG_TYPE_SIMPLE )
2102 {
2103 writeSimpleTag( tag, escapeAttributeValues( attributes ) );
2104 }
2105 else if ( tagType == TAG_TYPE_START )
2106 {
2107 writeStartTag( tag, escapeAttributeValues( attributes ) );
2108 }
2109 else if ( tagType == TAG_TYPE_END )
2110 {
2111 writeEndTag( tag );
2112 }
2113 else
2114 {
2115 String msg = "No type information for unknown event: '" + name + "', ignoring!";
2116 logMessage( "noTypeInfo", msg );
2117 }
2118 }
2119 }
2120
2121 private SinkEventAttributes escapeAttributeValues( SinkEventAttributes attributes )
2122 {
2123 SinkEventAttributeSet set = new SinkEventAttributeSet( attributes.getAttributeCount() );
2124
2125 Enumeration<?> names = attributes.getAttributeNames();
2126
2127 while ( names.hasMoreElements() )
2128 {
2129 Object name = names.nextElement();
2130
2131 set.addAttribute( name, escapeHTML( attributes.getAttribute( name ).toString() ) );
2132 }
2133
2134 return set;
2135 }
2136
2137
2138 @Override
2139 public void flush()
2140 {
2141 writer.flush();
2142 }
2143
2144
2145 @Override
2146 public void close()
2147 {
2148 writer.close();
2149
2150 if ( getLog().isWarnEnabled() && this.warnMessages != null )
2151 {
2152 for ( Map.Entry<String, Set<String>> entry : this.warnMessages.entrySet() )
2153 {
2154 for ( String msg : entry.getValue() )
2155 {
2156 getLog().warn( msg );
2157 }
2158 }
2159
2160 this.warnMessages = null;
2161 }
2162
2163 init();
2164 }
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175 protected void content( String text )
2176 {
2177
2178 String txt = escapeHTML( text );
2179 txt = StringUtils.replace( txt, "&#", "&#" );
2180 write( txt );
2181 }
2182
2183
2184
2185
2186
2187
2188 protected void verbatimContent( String text )
2189 {
2190 write( escapeHTML( text ) );
2191 }
2192
2193
2194
2195
2196
2197
2198
2199
2200 protected static String escapeHTML( String text )
2201 {
2202 return HtmlTools.escapeHTML( text, false );
2203 }
2204
2205
2206
2207
2208
2209
2210
2211
2212 protected static String encodeURL( String text )
2213 {
2214 return HtmlTools.encodeURL( text );
2215 }
2216
2217
2218 protected void write( String text )
2219 {
2220 if ( !this.tableCaptionXMLWriterStack.isEmpty() && this.tableCaptionXMLWriterStack.getLast() != null )
2221 {
2222 this.tableCaptionXMLWriterStack.getLast().writeMarkup( unifyEOLs( text ) );
2223 }
2224 else if ( !this.tableContentWriterStack.isEmpty() && this.tableContentWriterStack.getLast() != null )
2225 {
2226 this.tableContentWriterStack.getLast().write( unifyEOLs( text ) );
2227 }
2228 else
2229 {
2230 writer.write( unifyEOLs( text ) );
2231 }
2232 }
2233
2234
2235 @Override
2236 protected void writeStartTag( Tag t, MutableAttributeSet att, boolean isSimpleTag )
2237 {
2238 if ( this.tableCaptionXMLWriterStack.isEmpty() )
2239 {
2240 super.writeStartTag ( t, att, isSimpleTag );
2241 }
2242 else
2243 {
2244 String tag = ( getNameSpace() != null ? getNameSpace() + ":" : "" ) + t.toString();
2245 this.tableCaptionXMLWriterStack.getLast().startElement( tag );
2246
2247 if ( att != null )
2248 {
2249 Enumeration<?> names = att.getAttributeNames();
2250 while ( names.hasMoreElements() )
2251 {
2252 Object key = names.nextElement();
2253 Object value = att.getAttribute( key );
2254
2255 this.tableCaptionXMLWriterStack.getLast().addAttribute( key.toString(), value.toString() );
2256 }
2257 }
2258
2259 if ( isSimpleTag )
2260 {
2261 this.tableCaptionXMLWriterStack.getLast().endElement();
2262 }
2263 }
2264 }
2265
2266
2267 @Override
2268 protected void writeEndTag( Tag t )
2269 {
2270 if ( this.tableCaptionXMLWriterStack.isEmpty() )
2271 {
2272 super.writeEndTag( t );
2273 }
2274 else
2275 {
2276 this.tableCaptionXMLWriterStack.getLast().endElement();
2277 }
2278 }
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288 private void logMessage( String key, String msg )
2289 {
2290 final String mesg = "[XHTML Sink] " + msg;
2291 if ( getLog().isDebugEnabled() )
2292 {
2293 getLog().debug( mesg );
2294
2295 return;
2296 }
2297
2298 if ( warnMessages == null )
2299 {
2300 warnMessages = new HashMap<>();
2301 }
2302
2303 Set<String> set = warnMessages.get( key );
2304 if ( set == null )
2305 {
2306 set = new TreeSet<>();
2307 }
2308 set.add( mesg );
2309 warnMessages.put( key, set );
2310 }
2311 }