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