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