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.Sink;
42 import org.apache.maven.doxia.sink.SinkEventAttributes;
43 import org.apache.maven.doxia.util.DoxiaUtils;
44 import org.apache.maven.doxia.util.HtmlTools;
45
46 import org.codehaus.plexus.util.StringUtils;
47 import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
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 ( !headerRow && cellCountStack != null && !cellCountStack.isEmpty()
1541 && cellJustifStack != null && !cellJustifStack.isEmpty() && getCellJustif() != null )
1542 {
1543 int cellCount = getCellCount();
1544 if ( cellCount < getCellJustif().length )
1545 {
1546 Map<Integer, MutableAttributeSet> hash = new HashMap<>();
1547 hash.put( Sink.JUSTIFY_CENTER, SinkEventAttributeSet.CENTER );
1548 hash.put( Sink.JUSTIFY_LEFT, SinkEventAttributeSet.LEFT );
1549 hash.put( Sink.JUSTIFY_RIGHT, SinkEventAttributeSet.RIGHT );
1550 MutableAttributeSet atts = hash.get( getCellJustif()[cellCount] );
1551
1552 if ( attributes == null )
1553 {
1554 attributes = new SinkEventAttributeSet();
1555 }
1556 if ( atts != null )
1557 {
1558 attributes.addAttributes( atts );
1559 }
1560 }
1561 }
1562
1563 if ( attributes == null )
1564 {
1565 writeStartTag( t, null );
1566 }
1567 else
1568 {
1569 writeStartTag( t,
1570 SinkUtils.filterAttributes( attributes, SinkUtils.SINK_TD_ATTRIBUTES ) );
1571 }
1572 }
1573
1574
1575 @Override
1576 public void tableCell_()
1577 {
1578 tableCell_( false );
1579 }
1580
1581
1582 @Override
1583 public void tableHeaderCell_()
1584 {
1585 tableCell_( true );
1586 }
1587
1588
1589
1590
1591
1592
1593
1594
1595 private void tableCell_( boolean headerRow )
1596 {
1597 Tag t = ( headerRow ? HtmlMarkup.TH : HtmlMarkup.TD );
1598
1599 writeEndTag( t );
1600
1601 if ( !this.isCellJustifStack.isEmpty() && this.isCellJustifStack.getLast().equals( Boolean.TRUE )
1602 && !this.cellCountStack.isEmpty() )
1603 {
1604 int cellCount = Integer.parseInt( this.cellCountStack.removeLast().toString() );
1605 this.cellCountStack.addLast( ++cellCount );
1606 }
1607 }
1608
1609
1610
1611
1612
1613 @Override
1614 public void tableCaption()
1615 {
1616 tableCaption( null );
1617 }
1618
1619
1620
1621
1622
1623 @Override
1624 public void tableCaption( SinkEventAttributes attributes )
1625 {
1626 StringWriter sw = new StringWriter();
1627 this.tableCaptionWriterStack.addLast( sw );
1628 this.tableCaptionXMLWriterStack.addLast( new PrettyPrintXMLWriter( sw ) );
1629
1630
1631 MutableAttributeSet atts = SinkUtils.filterAttributes(
1632 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES );
1633
1634 writeStartTag( HtmlMarkup.CAPTION, atts );
1635 }
1636
1637
1638
1639
1640
1641 @Override
1642 public void tableCaption_()
1643 {
1644 writeEndTag( HtmlMarkup.CAPTION );
1645
1646 if ( !this.tableCaptionXMLWriterStack.isEmpty() && this.tableCaptionXMLWriterStack.getLast() != null )
1647 {
1648 this.tableCaptionStack.addLast( this.tableCaptionWriterStack.removeLast().toString() );
1649 this.tableCaptionXMLWriterStack.removeLast();
1650 }
1651 }
1652
1653
1654
1655
1656
1657 @Override
1658 public void anchor( String name )
1659 {
1660 anchor( name, null );
1661 }
1662
1663
1664
1665
1666
1667 @Override
1668 public void anchor( String name, SinkEventAttributes attributes )
1669 {
1670 if ( name == null )
1671 {
1672 throw new NullPointerException( "Anchor name cannot be null!" );
1673 }
1674
1675 if ( headFlag )
1676 {
1677 return;
1678 }
1679
1680 MutableAttributeSet atts = SinkUtils.filterAttributes(
1681 attributes, SinkUtils.SINK_BASE_ATTRIBUTES );
1682
1683 String id = name;
1684
1685 if ( !DoxiaUtils.isValidId( id ) )
1686 {
1687 id = DoxiaUtils.encodeId( name, true );
1688
1689 String msg = "Modified invalid anchor name: '" + name + "' to '" + id + "'";
1690 logMessage( "modifiedLink", msg );
1691 }
1692
1693 MutableAttributeSet att = new SinkEventAttributeSet();
1694 att.addAttribute( Attribute.NAME, id );
1695 att.addAttributes( atts );
1696
1697 writeStartTag( HtmlMarkup.A, att );
1698 }
1699
1700
1701
1702
1703
1704 @Override
1705 public void anchor_()
1706 {
1707 if ( !headFlag )
1708 {
1709 writeEndTag( HtmlMarkup.A );
1710 }
1711 }
1712
1713
1714 @Override
1715 public void link( String name )
1716 {
1717 link( name, null );
1718 }
1719
1720
1721 @Override
1722 public void link( String name, SinkEventAttributes attributes )
1723 {
1724 if ( attributes == null )
1725 {
1726 link( name, null, null );
1727 }
1728 else
1729 {
1730 String target = (String) attributes.getAttribute( Attribute.TARGET.toString() );
1731 MutableAttributeSet atts = SinkUtils.filterAttributes(
1732 attributes, SinkUtils.SINK_LINK_ATTRIBUTES );
1733
1734 link( name, target, atts );
1735 }
1736 }
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748 private void link( String href, String target, MutableAttributeSet attributes )
1749 {
1750 if ( href == null )
1751 {
1752 throw new NullPointerException( "Link name cannot be null!" );
1753 }
1754
1755 if ( headFlag )
1756 {
1757 return;
1758 }
1759
1760 MutableAttributeSet att = new SinkEventAttributeSet();
1761
1762 if ( DoxiaUtils.isExternalLink( href ) )
1763 {
1764 att.addAttribute( Attribute.CLASS, "externalLink" );
1765 }
1766
1767 att.addAttribute( Attribute.HREF, HtmlTools.escapeHTML( href ) );
1768
1769 if ( target != null )
1770 {
1771 att.addAttribute( Attribute.TARGET, target );
1772 }
1773
1774 if ( attributes != null )
1775 {
1776 attributes.removeAttribute( Attribute.HREF.toString() );
1777 attributes.removeAttribute( Attribute.TARGET.toString() );
1778 att.addAttributes( attributes );
1779 }
1780
1781 writeStartTag( HtmlMarkup.A, att );
1782 }
1783
1784
1785
1786
1787
1788 @Override
1789 public void link_()
1790 {
1791 if ( !headFlag )
1792 {
1793 writeEndTag( HtmlMarkup.A );
1794 }
1795 }
1796
1797
1798 @Override
1799 public void inline()
1800 {
1801 inline( null );
1802 }
1803
1804 private void inlineSemantics( SinkEventAttributes attributes, String semantic,
1805 List<Tag> tags, Tag tag )
1806 {
1807 if ( attributes.containsAttribute( SinkEventAttributes.SEMANTICS, semantic ) )
1808 {
1809 SinkEventAttributes attributesNoSemantics = ( SinkEventAttributes ) attributes.copyAttributes();
1810 attributesNoSemantics.removeAttribute( SinkEventAttributes.SEMANTICS );
1811 writeStartTag( tag, attributesNoSemantics );
1812 tags.add( 0, tag );
1813 }
1814 }
1815
1816
1817 @Override
1818 public void inline( SinkEventAttributes attributes )
1819 {
1820 if ( !headFlag )
1821 {
1822 List<Tag> tags = new ArrayList<>();
1823
1824 if ( attributes != null )
1825 {
1826 inlineSemantics( attributes, "emphasis", tags, HtmlMarkup.EM );
1827 inlineSemantics( attributes, "strong", tags, HtmlMarkup.STRONG );
1828 inlineSemantics( attributes, "small", tags, HtmlMarkup.SMALL );
1829 inlineSemantics( attributes, "line-through", tags, HtmlMarkup.S );
1830 inlineSemantics( attributes, "citation", tags, HtmlMarkup.CITE );
1831 inlineSemantics( attributes, "quote", tags, HtmlMarkup.Q );
1832 inlineSemantics( attributes, "definition", tags, HtmlMarkup.DFN );
1833 inlineSemantics( attributes, "abbreviation", tags, HtmlMarkup.ABBR );
1834 inlineSemantics( attributes, "italic", tags, HtmlMarkup.I );
1835 inlineSemantics( attributes, "bold", tags, HtmlMarkup.B );
1836 inlineSemantics( attributes, "monospaced", tags, HtmlMarkup.TT );
1837 inlineSemantics( attributes, "code", tags, HtmlMarkup.CODE );
1838 inlineSemantics( attributes, "variable", tags, HtmlMarkup.VAR );
1839 inlineSemantics( attributes, "sample", tags, HtmlMarkup.SAMP );
1840 inlineSemantics( attributes, "keyboard", tags, HtmlMarkup.KBD );
1841 inlineSemantics( attributes, "superscript", tags, HtmlMarkup.SUP );
1842 inlineSemantics( attributes, "subscript", tags, HtmlMarkup.SUB );
1843 inlineSemantics( attributes, "annotation", tags, HtmlMarkup.U );
1844 inlineSemantics( attributes, "bidirectionalOverride", tags, HtmlMarkup.BDO );
1845 inlineSemantics( attributes, "phrase", tags, HtmlMarkup.SPAN );
1846 inlineSemantics( attributes, "insert", tags, HtmlMarkup.INS );
1847 inlineSemantics( attributes, "delete", tags, HtmlMarkup.DEL );
1848 }
1849
1850 inlineStack.push( tags );
1851 }
1852 }
1853
1854
1855 @Override
1856 public void inline_()
1857 {
1858 if ( !headFlag )
1859 {
1860 for ( Tag tag: inlineStack.pop() )
1861 {
1862 writeEndTag( tag );
1863 }
1864 }
1865 }
1866
1867
1868
1869
1870
1871 @Override
1872 public void italic()
1873 {
1874 inline( SinkEventAttributeSet.Semantics.ITALIC );
1875 }
1876
1877
1878
1879
1880
1881 @Override
1882 public void italic_()
1883 {
1884 inline_();
1885 }
1886
1887
1888
1889
1890
1891 @Override
1892 public void bold()
1893 {
1894 inline( SinkEventAttributeSet.Semantics.BOLD );
1895 }
1896
1897
1898
1899
1900
1901 @Override
1902 public void bold_()
1903 {
1904 inline_();
1905 }
1906
1907
1908
1909
1910
1911 @Override
1912 public void monospaced()
1913 {
1914 inline( SinkEventAttributeSet.Semantics.MONOSPACED );
1915 }
1916
1917
1918
1919
1920
1921 @Override
1922 public void monospaced_()
1923 {
1924 inline_();
1925 }
1926
1927
1928
1929
1930
1931 @Override
1932 public void lineBreak()
1933 {
1934 lineBreak( null );
1935 }
1936
1937
1938
1939
1940
1941 @Override
1942 public void lineBreak( SinkEventAttributes attributes )
1943 {
1944 if ( headFlag || isVerbatimFlag() )
1945 {
1946 getTextBuffer().append( EOL );
1947 }
1948 else
1949 {
1950 MutableAttributeSet atts = SinkUtils.filterAttributes(
1951 attributes, SinkUtils.SINK_BR_ATTRIBUTES );
1952
1953 writeSimpleTag( HtmlMarkup.BR, atts );
1954 }
1955 }
1956
1957
1958 @Override
1959 public void pageBreak()
1960 {
1961 comment( " PB " );
1962 }
1963
1964
1965 @Override
1966 public void nonBreakingSpace()
1967 {
1968 if ( headFlag )
1969 {
1970 getTextBuffer().append( ' ' );
1971 }
1972 else
1973 {
1974 write( " " );
1975 }
1976 }
1977
1978
1979 @Override
1980 public void text( String text )
1981 {
1982 if ( headFlag )
1983 {
1984 getTextBuffer().append( text );
1985 }
1986 else if ( verbatimFlag )
1987 {
1988 verbatimContent( text );
1989 }
1990 else
1991 {
1992 content( text );
1993 }
1994 }
1995
1996
1997 @Override
1998 public void text( String text, SinkEventAttributes attributes )
1999 {
2000 text( text );
2001 }
2002
2003
2004 @Override
2005 public void rawText( String text )
2006 {
2007 if ( headFlag )
2008 {
2009 getTextBuffer().append( text );
2010 }
2011 else
2012 {
2013 write( text );
2014 }
2015 }
2016
2017
2018 @Override
2019 public void comment( String comment )
2020 {
2021 if ( comment != null )
2022 {
2023 final String originalComment = comment;
2024
2025
2026 while ( comment.contains( "--" ) )
2027 {
2028 comment = comment.replace( "--", "- -" );
2029 }
2030
2031 if ( comment.endsWith( "-" ) )
2032 {
2033 comment += " ";
2034 }
2035
2036 if ( !originalComment.equals( comment ) )
2037 {
2038 getLog().warn( "[Xhtml Sink] Modified invalid comment '" + originalComment + "' to '" + comment + "'" );
2039 }
2040
2041 final StringBuilder buffer = new StringBuilder( comment.length() + 7 );
2042
2043 buffer.append( LESS_THAN ).append( BANG ).append( MINUS ).append( MINUS );
2044 buffer.append( comment );
2045 buffer.append( MINUS ).append( MINUS ).append( GREATER_THAN );
2046
2047 write( buffer.toString() );
2048 }
2049 }
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
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
2088
2089
2090
2091
2092 @Override
2093 public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes )
2094 {
2095 if ( requiredParams == null || !( requiredParams[0] instanceof Integer ) )
2096 {
2097 String msg = "No type information for unknown event: '" + name + "', ignoring!";
2098 logMessage( "noTypeInfo", msg );
2099
2100 return;
2101 }
2102
2103 int tagType = (Integer) requiredParams[0];
2104
2105 if ( tagType == ENTITY_TYPE )
2106 {
2107 rawText( name );
2108
2109 return;
2110 }
2111
2112 if ( tagType == CDATA_TYPE )
2113 {
2114 rawText( EOL + "//<![CDATA[" + requiredParams[1] + "]]>" + EOL );
2115
2116 return;
2117 }
2118
2119 Tag tag = HtmlTools.getHtmlTag( name );
2120
2121 if ( tag == null )
2122 {
2123 String msg = "No HTML tag found for unknown event: '" + name + "', ignoring!";
2124 logMessage( "noHtmlTag", msg );
2125 }
2126 else
2127 {
2128 if ( tagType == TAG_TYPE_SIMPLE )
2129 {
2130 writeSimpleTag( tag, escapeAttributeValues( attributes ) );
2131 }
2132 else if ( tagType == TAG_TYPE_START )
2133 {
2134 writeStartTag( tag, escapeAttributeValues( attributes ) );
2135 }
2136 else if ( tagType == TAG_TYPE_END )
2137 {
2138 writeEndTag( tag );
2139 }
2140 else
2141 {
2142 String msg = "No type information for unknown event: '" + name + "', ignoring!";
2143 logMessage( "noTypeInfo", msg );
2144 }
2145 }
2146 }
2147
2148 private SinkEventAttributes escapeAttributeValues( SinkEventAttributes attributes )
2149 {
2150 SinkEventAttributeSet set = new SinkEventAttributeSet( attributes.getAttributeCount() );
2151
2152 Enumeration<?> names = attributes.getAttributeNames();
2153
2154 while ( names.hasMoreElements() )
2155 {
2156 Object name = names.nextElement();
2157
2158 set.addAttribute( name, escapeHTML( attributes.getAttribute( name ).toString() ) );
2159 }
2160
2161 return set;
2162 }
2163
2164
2165 @Override
2166 public void flush()
2167 {
2168 writer.flush();
2169 }
2170
2171
2172 @Override
2173 public void close()
2174 {
2175 writer.close();
2176
2177 if ( getLog().isWarnEnabled() && this.warnMessages != null )
2178 {
2179 for ( Map.Entry<String, Set<String>> entry : this.warnMessages.entrySet() )
2180 {
2181 for ( String msg : entry.getValue() )
2182 {
2183 getLog().warn( msg );
2184 }
2185 }
2186
2187 this.warnMessages = null;
2188 }
2189
2190 init();
2191 }
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202 protected void content( String text )
2203 {
2204
2205 String txt = escapeHTML( text );
2206 txt = StringUtils.replace( txt, "&#", "&#" );
2207 write( txt );
2208 }
2209
2210
2211
2212
2213
2214
2215 protected void verbatimContent( String text )
2216 {
2217 write( escapeHTML( text ) );
2218 }
2219
2220
2221
2222
2223
2224
2225
2226
2227 protected static String escapeHTML( String text )
2228 {
2229 return HtmlTools.escapeHTML( text, false );
2230 }
2231
2232
2233
2234
2235
2236
2237
2238
2239 protected static String encodeURL( String text )
2240 {
2241 return HtmlTools.encodeURL( text );
2242 }
2243
2244
2245 protected void write( String text )
2246 {
2247 if ( !this.tableCaptionXMLWriterStack.isEmpty() && this.tableCaptionXMLWriterStack.getLast() != null )
2248 {
2249 this.tableCaptionXMLWriterStack.getLast().writeMarkup( unifyEOLs( text ) );
2250 }
2251 else if ( !this.tableContentWriterStack.isEmpty() && this.tableContentWriterStack.getLast() != null )
2252 {
2253 this.tableContentWriterStack.getLast().write( unifyEOLs( text ) );
2254 }
2255 else
2256 {
2257 writer.write( unifyEOLs( text ) );
2258 }
2259 }
2260
2261
2262 @Override
2263 protected void writeStartTag( Tag t, MutableAttributeSet att, boolean isSimpleTag )
2264 {
2265 if ( this.tableCaptionXMLWriterStack.isEmpty() )
2266 {
2267 super.writeStartTag ( t, att, isSimpleTag );
2268 }
2269 else
2270 {
2271 String tag = ( getNameSpace() != null ? getNameSpace() + ":" : "" ) + t.toString();
2272 this.tableCaptionXMLWriterStack.getLast().startElement( tag );
2273
2274 if ( att != null )
2275 {
2276 Enumeration<?> names = att.getAttributeNames();
2277 while ( names.hasMoreElements() )
2278 {
2279 Object key = names.nextElement();
2280 Object value = att.getAttribute( key );
2281
2282 this.tableCaptionXMLWriterStack.getLast().addAttribute( key.toString(), value.toString() );
2283 }
2284 }
2285
2286 if ( isSimpleTag )
2287 {
2288 this.tableCaptionXMLWriterStack.getLast().endElement();
2289 }
2290 }
2291 }
2292
2293
2294 @Override
2295 protected void writeEndTag( Tag t )
2296 {
2297 if ( this.tableCaptionXMLWriterStack.isEmpty() )
2298 {
2299 super.writeEndTag( t );
2300 }
2301 else
2302 {
2303 this.tableCaptionXMLWriterStack.getLast().endElement();
2304 }
2305 }
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315 private void logMessage( String key, String msg )
2316 {
2317 final String mesg = "[XHTML Sink] " + msg;
2318 if ( getLog().isDebugEnabled() )
2319 {
2320 getLog().debug( mesg );
2321
2322 return;
2323 }
2324
2325 if ( warnMessages == null )
2326 {
2327 warnMessages = new HashMap<>();
2328 }
2329
2330 Set<String> set = warnMessages.get( key );
2331 if ( set == null )
2332 {
2333 set = new TreeSet<>();
2334 }
2335 set.add( mesg );
2336 warnMessages.put( key, set );
2337 }
2338 }