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