1 package org.apache.maven.reporting;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.commons.validator.EmailValidator;
23 import org.apache.commons.validator.UrlValidator;
24
25 import org.apache.maven.doxia.sink.Sink;
26 import org.apache.maven.doxia.util.HtmlTools;
27
28 import org.codehaus.plexus.util.StringUtils;
29
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.Iterator;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Properties;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 public abstract class AbstractMavenReportRenderer
53 implements MavenReportRenderer
54 {
55
56 protected Sink sink;
57
58
59 private int section;
60
61
62
63
64
65
66 public AbstractMavenReportRenderer( Sink sink )
67 {
68 this.sink = sink;
69 }
70
71
72 public void render()
73 {
74 sink.head();
75
76 sink.title();
77 text( getTitle() );
78 sink.title_();
79
80 sink.head_();
81
82 sink.body();
83 renderBody();
84 sink.body_();
85
86 sink.flush();
87
88 sink.close();
89 }
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116 protected void startSection( String name )
117 {
118 section = section + 1;
119
120 switch ( section )
121 {
122 case 1:
123 sink.section1();
124 sink.sectionTitle1();
125 break;
126 case 2:
127 sink.section2();
128 sink.sectionTitle2();
129 break;
130 case 3:
131 sink.section3();
132 sink.sectionTitle3();
133 break;
134 case 4:
135 sink.section4();
136 sink.sectionTitle4();
137 break;
138 case 5:
139 sink.section5();
140 sink.sectionTitle5();
141 break;
142
143 default:
144
145 break;
146 }
147
148 text( name );
149
150 switch ( section )
151 {
152 case 1:
153 sink.sectionTitle1_();
154 break;
155 case 2:
156 sink.sectionTitle2_();
157 break;
158 case 3:
159 sink.sectionTitle3_();
160 break;
161 case 4:
162 sink.sectionTitle4_();
163 break;
164 case 5:
165 sink.sectionTitle5_();
166 break;
167
168 default:
169
170 break;
171 }
172
173 sink.anchor( HtmlTools.encodeId( name ) );
174 sink.anchor_();
175 }
176
177
178
179
180
181
182
183
184
185
186
187 protected void endSection()
188 {
189 switch ( section )
190 {
191 case 1:
192 sink.section1_();
193 break;
194 case 2:
195 sink.section2_();
196 break;
197 case 3:
198 sink.section3_();
199 break;
200 case 4:
201 sink.section4_();
202 break;
203 case 5:
204 sink.section5_();
205 break;
206
207 default:
208
209 break;
210 }
211
212 section = section - 1;
213
214 if ( section < 0 )
215 {
216 throw new IllegalStateException( "Too many closing sections" );
217 }
218 }
219
220
221
222
223
224
225
226
227
228
229 protected void startTable()
230 {
231 startTable( new int[] {Sink.JUSTIFY_LEFT}, false );
232 }
233
234
235
236
237
238
239
240
241
242
243
244 protected void startTable( int[] justification, boolean grid )
245 {
246 sink.table();
247 sink.tableRows( justification, grid );
248 }
249
250
251
252
253
254
255 protected void endTable()
256 {
257 sink.tableRows_();
258 sink.table_();
259 }
260
261
262
263
264
265
266
267
268
269 protected void tableHeaderCell( String text )
270 {
271 sink.tableHeaderCell();
272
273 text( text );
274
275 sink.tableHeaderCell_();
276 }
277
278
279
280
281
282
283
284
285
286 protected void tableCell( String text )
287 {
288 tableCell( text, false );
289 }
290
291
292
293
294
295
296
297
298
299
300
301
302
303 protected void tableCell( String text, boolean asHtml )
304 {
305 sink.tableCell();
306
307 if ( asHtml )
308 {
309 sink.rawText( text );
310 }
311 else
312 {
313 linkPatternedText( text );
314 }
315
316 sink.tableCell_();
317 }
318
319
320
321
322
323
324
325
326
327
328 protected void tableRow( String[] content )
329 {
330 sink.tableRow();
331
332 if ( content != null )
333 {
334 for ( int i = 0; i < content.length; i++ )
335 {
336 tableCell( content[i] );
337 }
338 }
339
340 sink.tableRow_();
341 }
342
343
344
345
346
347
348
349
350
351
352 protected void tableHeader( String[] content )
353 {
354 sink.tableRow();
355
356 if ( content != null )
357 {
358 for ( int i = 0; i < content.length; i++ )
359 {
360 tableHeaderCell( content[i] );
361 }
362 }
363
364 sink.tableRow_();
365 }
366
367
368
369
370
371
372
373
374
375 protected void tableCaption( String caption )
376 {
377 sink.tableCaption();
378
379 text( caption );
380
381 sink.tableCaption_();
382 }
383
384
385
386
387
388
389
390
391
392
393
394
395
396 protected void paragraph( String paragraph )
397 {
398 sink.paragraph();
399
400 text( paragraph );
401
402 sink.paragraph_();
403 }
404
405
406
407
408
409
410
411
412
413
414 protected void link( String href, String name )
415 {
416 sink.link( href );
417
418 text( name );
419
420 sink.link_();
421 }
422
423
424
425
426
427
428
429
430 protected void text( String text )
431 {
432 if ( StringUtils.isEmpty( text ) )
433 {
434 sink.text( "-" );
435 }
436 else
437 {
438 sink.text( text );
439 }
440 }
441
442
443
444
445
446
447
448
449
450 protected void verbatimText( String text )
451 {
452 sink.verbatim( true );
453
454 text( text );
455
456 sink.verbatim_();
457 }
458
459
460
461
462
463
464
465
466
467
468
469 protected void verbatimLink( String text, String href )
470 {
471 if ( StringUtils.isEmpty( href ) )
472 {
473 verbatimText( text );
474 }
475 else
476 {
477 sink.verbatim( true );
478
479 link( href, text );
480
481 sink.verbatim_();
482 }
483 }
484
485
486
487
488
489
490
491 protected void javaScript( String jsCode )
492 {
493 sink.rawText( "<script type=\"text/javascript\">\n" + jsCode + "</script>" );
494 }
495
496
497
498
499
500
501
502
503
504
505
506 public void linkPatternedText( String text )
507 {
508 if ( StringUtils.isEmpty( text ) )
509 {
510 text( text );
511 }
512 else
513 {
514 List<String> segments = applyPattern( text );
515
516 if ( segments == null )
517 {
518 text( text );
519 }
520 else
521 {
522 for ( Iterator<String> it = segments.iterator(); it.hasNext(); )
523 {
524 String name = it.next();
525 String href = it.next();
526
527 if ( href == null )
528 {
529 text( name );
530 }
531 else
532 {
533 if ( getValidHref( href ) != null )
534 {
535 link( getValidHref( href ), name );
536 }
537 else
538 {
539 text( href );
540 }
541 }
542 }
543 }
544 }
545 }
546
547
548
549
550
551
552
553
554
555
556
557 protected static String createLinkPatternedText( String text, String href )
558 {
559 if ( text == null )
560 {
561 return text;
562 }
563
564 if ( href == null )
565 {
566 return text;
567 }
568
569 return '{' + text + ", " + href + '}';
570 }
571
572
573
574
575
576
577
578 protected static String propertiesToString( Properties props )
579 {
580 if ( props == null || props.isEmpty() )
581 {
582 return "";
583 }
584
585 StringBuilder sb = new StringBuilder();
586
587 for ( Map.Entry<?, ?> entry : props.entrySet() )
588 {
589 if ( sb.length() > 0 )
590 {
591 sb.append( ", " );
592 }
593
594 sb.append( entry.getKey() ).append( "=" ).append( entry.getValue() );
595 }
596
597 return sb.toString();
598 }
599
600
601
602
603
604
605
606
607
608
609
610
611
612 private static String getValidHref( String href )
613 {
614 if ( StringUtils.isEmpty( href ) )
615 {
616 return null;
617 }
618
619 href = href.trim();
620
621 String[] schemes = {"http", "https"};
622 UrlValidator urlValidator = new UrlValidator( schemes );
623 EmailValidator emailValidator = EmailValidator.getInstance();
624
625 if ( emailValidator.isValid( href )
626 || ( href.contains( "?" ) && emailValidator.isValid( href.substring( 0, href.indexOf( "?" ) ) ) ) )
627 {
628 return "mailto:" + href;
629 }
630 else if ( href.toLowerCase().startsWith( "mailto:" ) )
631 {
632 return href;
633 }
634 else if ( urlValidator.isValid( href ) )
635 {
636 return href;
637 }
638 else
639 {
640 String hrefTmp;
641 if ( !href.endsWith( "/" ) )
642 {
643 hrefTmp = href + "/index.html";
644 }
645 else
646 {
647 hrefTmp = href + "index.html";
648 }
649
650 if ( urlValidator.isValid( hrefTmp ) )
651 {
652 return href;
653 }
654
655 if ( href.startsWith( "./" ) )
656 {
657 if ( href.length() > 2 )
658 {
659 return href.substring( 2, href.length() );
660 }
661
662 return ".";
663 }
664
665 return null;
666 }
667 }
668
669
670
671
672
673
674
675
676 private static List<String> applyPattern( String text )
677 {
678 if ( StringUtils.isEmpty( text ) )
679 {
680 return null;
681 }
682
683
684
685 List<String> segments = new ArrayList<String>();
686
687
688 if ( text.indexOf( "${" ) != -1 )
689 {
690 int lastComma = text.lastIndexOf( "," );
691 int lastSemi = text.lastIndexOf( "}" );
692 if ( lastComma != -1 && lastSemi != -1 && lastComma < lastSemi )
693 {
694 segments.add( text.substring( lastComma + 1, lastSemi ).trim() );
695 segments.add( null );
696 }
697 else
698 {
699 segments.add( text );
700 segments.add( null );
701 }
702
703 return segments;
704 }
705
706 boolean inQuote = false;
707 int braceStack = 0;
708 int lastOffset = 0;
709
710 for ( int i = 0; i < text.length(); i++ )
711 {
712 char ch = text.charAt( i );
713
714 if ( ch == '\'' && !inQuote && braceStack == 0 )
715 {
716
717 if ( i + 1 < text.length() && text.charAt( i + 1 ) == '\'' )
718 {
719 i++;
720 segments.add( text.substring( lastOffset, i ) );
721 segments.add( null );
722 lastOffset = i + 1;
723 }
724 else
725 {
726 inQuote = true;
727 }
728 }
729 else
730 {
731 switch ( ch )
732 {
733 case '{':
734 if ( !inQuote )
735 {
736 if ( braceStack == 0 )
737 {
738 if ( i != 0 )
739 {
740 segments.add( text.substring( lastOffset, i ) );
741 segments.add( null );
742 }
743 lastOffset = i + 1;
744 }
745 braceStack++;
746 }
747 break;
748 case '}':
749 if ( !inQuote )
750 {
751 braceStack--;
752 if ( braceStack == 0 )
753 {
754 String subString = text.substring( lastOffset, i );
755 lastOffset = i + 1;
756
757 int lastComma = subString.lastIndexOf( "," );
758 if ( lastComma != -1 )
759 {
760 segments.add( subString.substring( 0, lastComma ).trim() );
761 segments.add( subString.substring( lastComma + 1 ).trim() );
762 }
763 else
764 {
765 segments.add( subString );
766 segments.add( null );
767 }
768 }
769 }
770 break;
771 case '\'':
772 inQuote = false;
773 break;
774 default:
775 break;
776 }
777 }
778 }
779
780 if ( !StringUtils.isEmpty( text.substring( lastOffset ) ) )
781 {
782 segments.add( text.substring( lastOffset ) );
783 segments.add( null );
784 }
785
786 if ( braceStack != 0 )
787 {
788 throw new IllegalArgumentException( "Unmatched braces in the pattern." );
789 }
790
791 if ( inQuote )
792 {
793
794
795 }
796
797 return Collections.unmodifiableList( segments );
798 }
799
800
801
802
803
804
805 public abstract String getTitle();
806
807
808
809
810 protected abstract void renderBody();
811 }