1 package org.apache.maven.report.projectinfo;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.doxia.sink.Sink;
23 import org.apache.maven.doxia.sink.SinkEventAttributeSet;
24 import org.apache.maven.model.Dependency;
25 import org.apache.maven.project.MavenProject;
26 import org.apache.maven.reporting.MavenReportException;
27 import org.codehaus.plexus.util.StringUtils;
28
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.Comparator;
32 import java.util.Iterator;
33 import java.util.List;
34 import java.util.Locale;
35 import java.util.Map;
36 import java.util.TreeMap;
37
38
39
40
41
42
43
44
45
46
47
48 public class DependencyConvergenceReport
49 extends AbstractProjectInfoReport
50 {
51 private static final int PERCENTAGE = 100;
52
53
54
55
56
57
58
59
60
61
62
63
64
65 private List reactorProjects;
66
67
68
69
70
71
72 public String getOutputName()
73 {
74 return "dependency-convergence";
75 }
76
77 protected String getI18Nsection()
78 {
79 return "dependency-convergence";
80 }
81
82
83 public boolean canGenerateReport()
84 {
85
86 return reactorProjects.size() > 1;
87 }
88
89
90
91
92
93
94 protected void executeReport( Locale locale )
95 throws MavenReportException
96 {
97 Sink sink = getSink();
98
99 sink.head();
100 sink.title();
101 sink.text( getI18nString( locale, "title" ) );
102 sink.title_();
103 sink.head_();
104
105 sink.body();
106
107 sink.section1();
108
109 sink.sectionTitle1();
110 sink.text( getI18nString( locale, "title" ) );
111 sink.sectionTitle1_();
112
113 Map dependencyMap = getDependencyMap();
114
115
116 generateLegend( locale, sink );
117
118 sink.lineBreak();
119
120
121 generateStats( locale, sink, dependencyMap );
122
123 sink.section1_();
124
125
126 generateConvergence( locale, sink, dependencyMap );
127
128 sink.body_();
129 sink.flush();
130 sink.close();
131 }
132
133
134
135
136
137
138
139
140
141
142
143
144 private void generateConvergence( Locale locale, Sink sink, Map dependencyMap )
145 {
146 sink.section2();
147
148 sink.sectionTitle2();
149 sink.text( getI18nString( locale, "convergence.caption" ) );
150 sink.sectionTitle2_();
151
152 Iterator it = dependencyMap.keySet().iterator();
153 while ( it.hasNext() )
154 {
155 String key = (String) it.next();
156 List depList = (List) dependencyMap.get( key );
157
158 sink.section3();
159 sink.sectionTitle3();
160 sink.text( key );
161 sink.sectionTitle3_();
162
163 generateDependencyDetails( sink, depList );
164
165 sink.section3_();
166 }
167
168 sink.section2_();
169 }
170
171
172
173
174
175
176
177 private void generateDependencyDetails( Sink sink, List depList )
178 {
179 sink.table();
180
181 Map artifactMap = getSortedUniqueArtifactMap( depList );
182
183 sink.tableRow();
184
185 sink.tableCell( );
186 if ( artifactMap.size() > 1 )
187 {
188 iconError( sink );
189 }
190 else
191 {
192 iconSuccess( sink );
193 }
194 sink.tableCell_();
195
196 sink.tableCell();
197
198 sink.table();
199
200 Iterator it = artifactMap.keySet().iterator();
201 while ( it.hasNext() )
202 {
203 String version = (String) it.next();
204 sink.tableRow();
205 sink.tableCell( new SinkEventAttributeSet( new String[] {SinkEventAttributeSet.WIDTH, "25%"} ) );
206 sink.text( version );
207 sink.tableCell_();
208
209 sink.tableCell();
210 generateVersionDetails( sink, artifactMap, version );
211 sink.tableCell_();
212
213 sink.tableRow_();
214 }
215 sink.table_();
216 sink.tableCell_();
217
218 sink.tableRow_();
219
220 sink.table_();
221 }
222
223 private void generateVersionDetails( Sink sink, Map artifactMap, String version )
224 {
225 sink.numberedList( 1 );
226 List depList = (List) artifactMap.get( version );
227 Collections.sort( depList, new ReverseDependencyLinkComparator() );
228 Iterator it = depList.iterator();
229 while ( it.hasNext() )
230 {
231 ReverseDependencyLink rdl = (ReverseDependencyLink) it.next();
232 sink.numberedListItem();
233 if ( StringUtils.isNotEmpty( rdl.project.getUrl() ) )
234 {
235 sink.link( rdl.project.getUrl() );
236 }
237 sink.text( rdl.project.getGroupId() + ":" + rdl.project.getArtifactId() );
238 if ( StringUtils.isNotEmpty( rdl.project.getUrl() ) )
239 {
240 sink.link_();
241 }
242 sink.numberedListItem_();
243 }
244 sink.numberedList_();
245 }
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266 private Map getSortedUniqueArtifactMap( List depList )
267 {
268 Map uniqueArtifactMap = new TreeMap();
269
270 Iterator it = depList.iterator();
271 while ( it.hasNext() )
272 {
273 ReverseDependencyLink rdl = (ReverseDependencyLink) it.next();
274 String key = rdl.getDependency().getVersion();
275 List projectList = (List) uniqueArtifactMap.get( key );
276 if ( projectList == null )
277 {
278 projectList = new ArrayList();
279 }
280 projectList.add( rdl );
281 uniqueArtifactMap.put( key, projectList );
282 }
283
284 return uniqueArtifactMap;
285 }
286
287
288
289
290
291
292
293 private void generateLegend( Locale locale, Sink sink )
294 {
295 sink.table();
296 sink.tableCaption();
297 sink.bold();
298 sink.text( getI18nString( locale, "legend" ) );
299 sink.bold_();
300 sink.tableCaption_();
301
302 sink.tableRow();
303
304 sink.tableCell( );
305 iconSuccess( sink );
306 sink.tableCell_();
307 sink.tableCell();
308 sink.text( getI18nString( locale, "legend.shared" ) );
309 sink.tableCell_();
310
311 sink.tableRow_();
312
313 sink.tableRow();
314
315 sink.tableCell( );
316 iconError( sink );
317 sink.tableCell_();
318 sink.tableCell();
319 sink.text( getI18nString( locale, "legend.different" ) );
320 sink.tableCell_();
321
322 sink.tableRow_();
323
324 sink.table_();
325 }
326
327
328
329
330
331
332
333
334 private void generateStats( Locale locale, Sink sink, Map dependencyMap )
335 {
336 int depCount = dependencyMap.size();
337 int artifactCount = 0;
338 int snapshotCount = 0;
339
340 Iterator it = dependencyMap.values().iterator();
341 while ( it.hasNext() )
342 {
343 List depList = (List) it.next();
344 Map artifactMap = getSortedUniqueArtifactMap( depList );
345 snapshotCount += countSnapshots( artifactMap );
346 artifactCount += artifactMap.size();
347 }
348
349 int convergence = (int) ( ( (double) depCount / (double) artifactCount ) * PERCENTAGE );
350
351
352 sink.table();
353 sink.tableCaption();
354 sink.bold();
355 sink.text( getI18nString( locale, "stats.caption" ) );
356 sink.bold_();
357 sink.tableCaption_();
358
359 sink.tableRow();
360 sink.tableHeaderCell( );
361 sink.text( getI18nString( locale, "stats.subprojects" ) );
362 sink.tableHeaderCell_();
363 sink.tableCell();
364 sink.text( String.valueOf( reactorProjects.size() ) );
365 sink.tableCell_();
366 sink.tableRow_();
367
368 sink.tableRow();
369 sink.tableHeaderCell( );
370 sink.text( getI18nString( locale, "stats.dependencies" ) );
371 sink.tableHeaderCell_();
372 sink.tableCell();
373 sink.text( String.valueOf( depCount ) );
374 sink.tableCell_();
375 sink.tableRow_();
376
377 sink.tableRow();
378 sink.tableHeaderCell( );
379 sink.text( getI18nString( locale, "stats.artifacts" ) );
380 sink.tableHeaderCell_();
381 sink.tableCell();
382 sink.text( String.valueOf( artifactCount ) );
383 sink.tableCell_();
384 sink.tableRow_();
385
386 sink.tableRow();
387 sink.tableHeaderCell( );
388 sink.text( getI18nString( locale, "stats.snapshots" ) );
389 sink.tableHeaderCell_();
390 sink.tableCell();
391 sink.text( String.valueOf( snapshotCount ) );
392 sink.tableCell_();
393 sink.tableRow_();
394
395 sink.tableRow();
396 sink.tableHeaderCell( );
397 sink.text( getI18nString( locale, "stats.convergence" ) );
398 sink.tableHeaderCell_();
399 sink.tableCell();
400 if ( convergence < PERCENTAGE )
401 {
402 iconError( sink );
403 }
404 else
405 {
406 iconSuccess( sink );
407 }
408 sink.nonBreakingSpace();
409 sink.bold();
410 sink.text( String.valueOf( convergence ) + "%" );
411 sink.bold_();
412 sink.tableCell_();
413 sink.tableRow_();
414
415 sink.tableRow();
416 sink.tableHeaderCell( );
417 sink.text( getI18nString( locale, "stats.readyrelease" ) );
418 sink.tableHeaderCell_();
419 sink.tableCell();
420 if ( convergence >= PERCENTAGE && snapshotCount <= 0 )
421 {
422 iconSuccess( sink );
423 sink.nonBreakingSpace();
424 sink.bold();
425 sink.text( getI18nString( locale, "stats.readyrelease.success" ) );
426 sink.bold_();
427 }
428 else
429 {
430 iconError( sink );
431 sink.nonBreakingSpace();
432 sink.bold();
433 sink.text( getI18nString( locale, "stats.readyrelease.error" ) );
434 sink.bold_();
435 if ( convergence < PERCENTAGE )
436 {
437 sink.lineBreak();
438 sink.text( getI18nString( locale, "stats.readyrelease.error.convergence" ) );
439 }
440 if ( snapshotCount > 0 )
441 {
442 sink.lineBreak();
443 sink.text( getI18nString( locale, "stats.readyrelease.error.snapshots" ) );
444 }
445 }
446 sink.tableCell_();
447 sink.tableRow_();
448
449 sink.table_();
450 }
451
452 private int countSnapshots( Map artifactMap )
453 {
454 int count = 0;
455 Iterator it = artifactMap.keySet().iterator();
456 while ( it.hasNext() )
457 {
458 String version = (String) it.next();
459 boolean isReactorProject = false;
460
461 Iterator iterator = ( (List) artifactMap.get( version ) ).iterator();
462
463
464
465 if ( iterator.hasNext() )
466 {
467 ReverseDependencyLink rdl = (ReverseDependencyLink) iterator.next();
468 if ( isReactorProject( rdl.getDependency() ) )
469 {
470 isReactorProject = true;
471 }
472 }
473
474 if ( version.endsWith( "-SNAPSHOT" ) && !isReactorProject )
475 {
476 count++;
477 }
478 }
479 return count;
480 }
481
482
483
484
485
486
487
488 private boolean isReactorProject( Dependency dependency )
489 {
490 Iterator iterator = reactorProjects.iterator();
491 while ( iterator.hasNext() )
492 {
493 MavenProject mavenProject = (MavenProject) iterator.next();
494 if ( mavenProject.getGroupId().equals( dependency.getGroupId() )
495 && mavenProject.getArtifactId().equals( dependency.getArtifactId() ) )
496 {
497 if ( getLog().isDebugEnabled() )
498 {
499 getLog().debug( dependency + " is a reactor project" );
500 }
501 return true;
502 }
503 }
504 return false;
505 }
506
507 private void iconSuccess( Sink sink )
508 {
509 sink.figure();
510 sink.figureCaption();
511 sink.text( "success" );
512 sink.figureCaption_();
513 sink.figureGraphics( "images/icon_success_sml.gif" );
514 sink.figure_();
515 }
516
517 private void iconError( Sink sink )
518 {
519 sink.figure();
520 sink.figureCaption();
521 sink.text( "error" );
522 sink.figureCaption_();
523 sink.figureGraphics( "images/icon_error_sml.gif" );
524 sink.figure_();
525 }
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546 private Map getDependencyMap()
547 {
548 Iterator it = reactorProjects.iterator();
549
550 Map dependencyMap = new TreeMap();
551
552 while ( it.hasNext() )
553 {
554 MavenProject reactorProject = (MavenProject) it.next();
555
556 Iterator itdep = reactorProject.getDependencies().iterator();
557 while ( itdep.hasNext() )
558 {
559 Dependency dep = (Dependency) itdep.next();
560 String key = dep.getGroupId() + ":" + dep.getArtifactId();
561 List depList = (List) dependencyMap.get( key );
562 if ( depList == null )
563 {
564 depList = new ArrayList();
565 }
566 depList.add( new ReverseDependencyLink( dep, reactorProject ) );
567 dependencyMap.put( key, depList );
568 }
569 }
570
571 return dependencyMap;
572 }
573
574
575
576
577 private static class ReverseDependencyLink
578 {
579 private Dependency dependency;
580
581 protected MavenProject project;
582
583 ReverseDependencyLink( Dependency dependency, MavenProject project )
584 {
585 this.dependency = dependency;
586 this.project = project;
587 }
588
589 public Dependency getDependency()
590 {
591 return dependency;
592 }
593
594 public MavenProject getProject()
595 {
596 return project;
597 }
598
599
600 public String toString()
601 {
602 return project.getId();
603 }
604 }
605
606
607
608
609 static class ReverseDependencyLinkComparator
610 implements Comparator
611 {
612
613 public int compare( Object o1, Object o2 )
614 {
615 if ( o1 instanceof ReverseDependencyLink && o2 instanceof ReverseDependencyLink )
616 {
617 ReverseDependencyLink p1 = (ReverseDependencyLink) o1;
618 ReverseDependencyLink p2 = (ReverseDependencyLink) o2;
619 return p1.getProject().getId().compareTo( p2.getProject().getId() );
620 }
621
622 return 0;
623 }
624 }
625 }