1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.apache.maven.plugins.linkcheck;
16
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Locale;
20
21 import org.apache.commons.io.FilenameUtils;
22
23 import org.apache.maven.doxia.linkcheck.model.LinkcheckFile;
24 import org.apache.maven.doxia.linkcheck.model.LinkcheckFileResult;
25 import org.apache.maven.doxia.linkcheck.model.LinkcheckModel;
26 import org.apache.maven.doxia.sink.Sink;
27
28 import org.codehaus.plexus.i18n.I18N;
29 import org.codehaus.plexus.util.StringUtils;
30
31
32
33
34
35
36 public class LinkcheckReportGenerator
37 {
38 private final I18N i18n;
39
40 private String httpMethod;
41 private boolean offline;
42 private String[] excludedLinks;
43 private Integer[] excludedHttpStatusErrors;
44 private Integer[] excludedHttpStatusWarnings;
45 private String[] excludedPages;
46 private boolean httpFollowRedirect;
47
48
49
50
51
52 public LinkcheckReportGenerator( I18N i18n )
53 {
54 this.i18n = i18n;
55 }
56
57
58
59
60
61 public void setExcludedHttpStatusErrors( Integer[] excludedHttpStatusErrors )
62 {
63 this.excludedHttpStatusErrors = excludedHttpStatusErrors;
64 }
65
66
67
68
69
70 public void setExcludedHttpStatusWarnings( Integer[] excludedHttpStatusWarnings )
71 {
72 this.excludedHttpStatusWarnings = excludedHttpStatusWarnings;
73 }
74
75
76
77
78
79 public void setExcludedLinks( String[] excludedLinks )
80 {
81 this.excludedLinks = excludedLinks;
82 }
83
84
85
86
87
88 public void setExcludedPages( String[] excludedPages )
89 {
90 this.excludedPages = excludedPages;
91 }
92
93
94
95
96
97 public void setHttpFollowRedirect( boolean httpFollowRedirect )
98 {
99 this.httpFollowRedirect = httpFollowRedirect;
100 }
101
102
103
104
105
106 public void setHttpMethod( String httpMethod )
107 {
108 this.httpMethod = httpMethod;
109 }
110
111
112
113
114
115 public void setOffline( boolean offline )
116 {
117 this.offline = offline;
118 }
119
120
121
122
123
124
125
126
127
128 public void generateReport( Locale locale, LinkcheckModel linkcheckModel, Sink sink )
129 {
130 String name = i18n.getString( "linkcheck-report", locale, "report.linkcheck.name" );
131
132 sink.head();
133 sink.title();
134 sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.title" ) );
135 sink.title_();
136 sink.head_();
137
138 sink.body();
139
140 if ( linkcheckModel == null )
141 {
142 sink.section1();
143 sink.sectionTitle1();
144 sink.text( name );
145 sink.sectionTitle1_();
146
147 sink.paragraph();
148 sink.rawText( i18n.getString( "linkcheck-report", locale, "report.linkcheck.empty" ) );
149 sink.paragraph_();
150
151 sink.section1_();
152
153 sink.body_();
154 sink.flush();
155 sink.close();
156
157 return;
158 }
159
160
161 sink.section1();
162 sink.sectionTitle1();
163 sink.text( name );
164 sink.sectionTitle1_();
165
166 sink.paragraph();
167 sink.rawText( i18n.getString( "linkcheck-report", locale, "report.linkcheck.overview" ) );
168 sink.paragraph_();
169
170 sink.section1_();
171
172
173 generateSummarySection( locale, linkcheckModel, sink );
174
175 if ( linkcheckModel.getFiles().size() > 0 )
176 {
177
178 generateDetailsSection( locale, linkcheckModel, sink );
179 }
180
181 sink.body_();
182 sink.flush();
183 sink.close();
184 }
185
186 private void generateSummarySection( Locale locale, LinkcheckModel linkcheckModel, Sink sink )
187 {
188
189 List linkcheckFiles = linkcheckModel.getFiles();
190
191 int totalFiles = linkcheckFiles.size();
192
193 int totalLinks = 0;
194 int totalValidLinks = 0;
195 int totalErrorLinks = 0;
196 int totalWarningLinks = 0;
197 for ( Iterator it = linkcheckFiles.iterator(); it.hasNext(); )
198 {
199 LinkcheckFile linkcheckFile = (LinkcheckFile) it.next();
200
201 totalLinks += linkcheckFile.getNumberOfLinks();
202 totalValidLinks += linkcheckFile.getNumberOfLinks( LinkcheckFileResult.VALID_LEVEL );
203 totalErrorLinks += linkcheckFile.getNumberOfLinks( LinkcheckFileResult.ERROR_LEVEL );
204 totalWarningLinks += linkcheckFile.getNumberOfLinks( LinkcheckFileResult.WARNING_LEVEL );
205 }
206
207 sink.section1();
208 sink.sectionTitle1();
209 sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary" ) );
210 sink.sectionTitle1_();
211
212
213 sink.paragraph();
214 sink.rawText( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.overview1" ) );
215 sink.paragraph_();
216
217 sink.table();
218
219 sink.tableRow();
220 sink.tableHeaderCell();
221 sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.parameter" ) );
222 sink.tableHeaderCell_();
223 sink.tableHeaderCell();
224 sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.value" ) );
225 sink.tableHeaderCell_();
226 sink.tableRow_();
227
228 sink.tableRow();
229 sink.tableCell();
230 sink.rawText(
231 i18n.getString( "linkcheck-report", locale,
232 "report.linkcheck.summary.table.httpFollowRedirect" ) );
233 sink.tableCell_();
234 sink.tableCell();
235 sink.text( String.valueOf( httpFollowRedirect ) );
236 sink.tableCell_();
237 sink.tableRow_();
238
239 sink.tableRow();
240 sink.tableCell();
241 sink
242 .rawText(
243 i18n
244 .getString( "linkcheck-report", locale, "report.linkcheck.summary.table.httpMethod" ) );
245 sink.tableCell_();
246 sink.tableCell();
247 if ( StringUtils.isEmpty( httpMethod ) )
248 {
249 sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.none" ) );
250 }
251 else
252 {
253 sink.text( httpMethod );
254 }
255 sink.tableCell_();
256 sink.tableRow_();
257
258 sink.tableRow();
259 sink.tableCell();
260 sink.rawText(
261 i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.offline" ) );
262 sink.tableCell_();
263 sink.tableCell();
264 sink.text( String.valueOf( offline ) );
265 sink.tableCell_();
266 sink.tableRow_();
267
268 sink.tableRow();
269 sink.tableCell();
270 sink.rawText(
271 i18n.getString( "linkcheck-report", locale,
272 "report.linkcheck.summary.table.excludedPages" ) );
273 sink.tableCell_();
274 sink.tableCell();
275 if ( excludedPages == null || excludedPages.length == 0 )
276 {
277 sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.none" ) );
278 }
279 else
280 {
281 sink.text( StringUtils.join( excludedPages, "," ) );
282 }
283 sink.tableCell_();
284 sink.tableRow_();
285
286 sink.tableRow();
287 sink.tableCell();
288 sink.rawText(
289 i18n.getString( "linkcheck-report", locale,
290 "report.linkcheck.summary.table.excludedLinks" ) );
291 sink.tableCell_();
292 sink.tableCell();
293 if ( excludedLinks == null || excludedLinks.length == 0 )
294 {
295 sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.none" ) );
296 }
297 else
298 {
299 sink.text( StringUtils.join( excludedLinks, "," ) );
300 }
301 sink.tableCell_();
302 sink.tableRow_();
303
304 sink.tableRow();
305 sink.tableCell();
306 sink.rawText(
307 i18n.getString( "linkcheck-report", locale,
308 "report.linkcheck.summary.table.excludedHttpStatusErrors" ) );
309 sink.tableCell_();
310 sink.tableCell();
311 if ( excludedHttpStatusErrors == null || excludedHttpStatusErrors.length == 0 )
312 {
313 sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.none" ) );
314 }
315 else
316 {
317 sink.text( toString( excludedHttpStatusErrors ) );
318 }
319 sink.tableCell_();
320 sink.tableRow_();
321
322 sink.tableRow();
323 sink.tableCell();
324 sink.rawText(
325 i18n.getString( "linkcheck-report", locale,
326 "report.linkcheck.summary.table.excludedHttpStatusWarnings" ) );
327 sink.tableCell_();
328 sink.tableCell();
329 if ( excludedHttpStatusWarnings == null || excludedHttpStatusWarnings.length == 0 )
330 {
331 sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.none" ) );
332 }
333 else
334 {
335 sink.text( toString( excludedHttpStatusWarnings ) );
336 }
337 sink.tableCell_();
338 sink.tableRow_();
339
340 sink.table_();
341
342
343 sink.paragraph();
344 sink.rawText( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.overview2" ) );
345 sink.paragraph_();
346
347 sink.table();
348
349
350 generateTableHeader( locale, false, sink );
351
352
353 sink.tableRow();
354
355 sink.tableCell();
356 sink.bold();
357 sink.text( totalFiles + "" );
358 sink.bold_();
359 sink.tableCell_();
360 sink.tableCell();
361 sink.bold();
362 sink.text( totalLinks + "" );
363 sink.bold_();
364 sink.tableCell_();
365 sink.tableCell();
366 sink.bold();
367 sink.text( String.valueOf( totalValidLinks ) );
368 sink.bold_();
369 sink.tableCell_();
370 sink.tableCell();
371 sink.bold();
372 sink.text( String.valueOf( totalWarningLinks ) );
373 sink.bold_();
374 sink.tableCell_();
375 sink.tableCell();
376 sink.bold();
377 sink.text( String.valueOf( totalErrorLinks ) );
378 sink.bold_();
379 sink.tableCell_();
380
381 sink.tableRow_();
382
383 sink.table_();
384
385 sink.section1_();
386 }
387
388 private void generateDetailsSection( Locale locale, LinkcheckModel linkcheckModel, Sink sink )
389 {
390 sink.section1();
391 sink.sectionTitle1();
392 sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.detail" ) );
393 sink.sectionTitle1_();
394
395 sink.paragraph();
396 sink.rawText( i18n.getString( "linkcheck-report", locale, "report.linkcheck.detail.overview" ) );
397 sink.paragraph_();
398
399 sink.table();
400
401
402 generateTableHeader( locale, true, sink );
403
404
405 List linkcheckFiles = linkcheckModel.getFiles();
406 for ( Iterator it = linkcheckFiles.iterator(); it.hasNext(); )
407 {
408 LinkcheckFile linkcheckFile = (LinkcheckFile) it.next();
409
410 sink.tableRow();
411
412 sink.tableCell();
413 if ( linkcheckFile.getUnsuccessful() == 0 )
414 {
415 iconValid( locale, sink );
416 }
417 else
418 {
419 iconError( locale, sink );
420 }
421 sink.tableCell_();
422
423
424
425 sink.tableCell();
426 sink.link( linkcheckFile.getRelativePath() );
427 sink.text( linkcheckFile.getRelativePath() );
428 sink.link_();
429 sink.tableCell_();
430 sink.tableCell();
431 sink.text( String.valueOf( linkcheckFile.getNumberOfLinks() ) );
432 sink.tableCell_();
433 sink.tableCell();
434 sink.text( String.valueOf( linkcheckFile.getNumberOfLinks( LinkcheckFileResult.VALID_LEVEL ) ) );
435 sink.tableCell_();
436 sink.tableCell();
437 sink.text( String.valueOf( linkcheckFile.getNumberOfLinks( LinkcheckFileResult.WARNING_LEVEL ) ) );
438 sink.tableCell_();
439 sink.tableCell();
440 sink.text( String.valueOf( linkcheckFile.getNumberOfLinks( LinkcheckFileResult.ERROR_LEVEL ) ) );
441 sink.tableCell_();
442
443 sink.tableRow_();
444
445
446 if ( linkcheckFile.getUnsuccessful() != 0 )
447 {
448 sink.tableRow();
449
450 sink.tableCell();
451 sink.text( "" );
452 sink.tableCell_();
453
454
455 sink.rawText( "<td colspan=\"5\">" );
456
457 sink.table();
458
459 for ( Iterator it2 = linkcheckFile.getResults().iterator(); it2.hasNext(); )
460 {
461 LinkcheckFileResult linkcheckFileResult = (LinkcheckFileResult) it2.next();
462
463 if ( linkcheckFileResult.getStatusLevel() == LinkcheckFileResult.VALID_LEVEL )
464 {
465 continue;
466 }
467
468 sink.tableRow();
469
470 sink.tableCell();
471 if ( linkcheckFileResult.getStatusLevel() == LinkcheckFileResult.WARNING_LEVEL )
472 {
473 iconWarning( locale, sink );
474 }
475 else if ( linkcheckFileResult.getStatusLevel() == LinkcheckFileResult.ERROR_LEVEL )
476 {
477 iconError( locale, sink );
478 }
479 sink.tableCell_();
480
481 sink.tableCell();
482 sink.italic();
483 if ( linkcheckFileResult.getTarget().startsWith( "#" ) )
484 {
485 sink.link( linkcheckFile.getRelativePath() + linkcheckFileResult.getTarget() );
486 }
487 else if ( linkcheckFileResult.getTarget().startsWith( "." ) )
488 {
489
490 String absolutePath = FilenameUtils.getFullPath( linkcheckFile.getRelativePath() )
491 + linkcheckFileResult.getTarget();
492 String normalizedPath = FilenameUtils.normalize( absolutePath );
493 if ( normalizedPath == null )
494 {
495 normalizedPath = absolutePath;
496 }
497 sink.link( normalizedPath );
498 }
499 else
500 {
501 sink.link( linkcheckFileResult.getTarget() );
502 }
503
504
505 sink.text( linkcheckFileResult.getTarget() );
506 sink.link_();
507 sink.text( ": " );
508 sink.text( linkcheckFileResult.getErrorMessage() );
509 sink.italic_();
510 sink.tableCell_();
511
512 sink.tableRow_();
513 }
514
515 sink.table_();
516
517 sink.tableCell_();
518
519 sink.tableRow_();
520 }
521 }
522
523 sink.table_();
524
525 sink.section1_();
526 }
527
528 private void generateTableHeader( Locale locale, boolean detail, Sink sink )
529 {
530 sink.tableRow();
531 if ( detail )
532 {
533 sink.rawText( "<th rowspan=\"2\">" );
534 sink.text( "" );
535 sink.tableHeaderCell_();
536 }
537 sink.rawText( "<th rowspan=\"2\">" );
538 sink.text(
539 detail ? i18n.getString( "linkcheck-report", locale,
540 "report.linkcheck.detail.table.documents" )
541 : i18n.getString( "linkcheck-report", locale,
542 "report.linkcheck.summary.table.documents" ) );
543 sink.tableHeaderCell_();
544
545 sink.rawText( "<th colspan=\"4\" align=\"center\">" );
546 sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.table.links" ) );
547 sink.tableHeaderCell_();
548 sink.tableRow_();
549
550 sink.tableRow();
551 sink.tableHeaderCell();
552 sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.table.totalLinks" ) );
553 sink.tableHeaderCell_();
554 sink.tableHeaderCell();
555 iconValid( locale, sink );
556 sink.tableHeaderCell_();
557 sink.tableHeaderCell();
558 iconWarning( locale, sink );
559 sink.tableHeaderCell_();
560 sink.tableHeaderCell();
561 iconError( locale, sink );
562 sink.tableHeaderCell_();
563 sink.tableRow_();
564 }
565
566 private void iconError( Locale locale, Sink sink )
567 {
568 sink.figure();
569 sink.figureCaption();
570 sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.icon.error" ) );
571 sink.figureCaption_();
572 sink.figureGraphics( LinkcheckReport.ICON_ERROR );
573 sink.figure_();
574 }
575
576 private void iconValid( Locale locale, Sink sink )
577 {
578 sink.figure();
579 sink.figureCaption();
580 sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.icon.valid" ) );
581 sink.figureCaption_();
582 sink.figureGraphics( LinkcheckReport.ICON_SUCCESS );
583 sink.figure_();
584 }
585
586 private void iconWarning( Locale locale, Sink sink )
587 {
588 sink.figure();
589 sink.figureCaption();
590 sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.icon.warning" ) );
591 sink.figureCaption_();
592 sink.figureGraphics( LinkcheckReport.ICON_WARNING );
593 sink.figure_();
594 }
595
596
597
598
599
600
601
602
603
604
605
606 private static String toString( Object[] a )
607 {
608 if ( a == null || a.length == 0 )
609 {
610 return "";
611 }
612
613 StringBuffer buf = new StringBuffer();
614 buf.append( a[0] );
615
616 for ( int i = 1; i < a.length; i++ )
617 {
618 buf.append( ", " );
619 buf.append( a[i] );
620 }
621
622 return buf.toString();
623 }
624 }