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 java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.Comparator;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Locale;
31 import java.util.Map;
32 import java.util.Set;
33 import java.util.TreeMap;
34
35 import org.apache.maven.artifact.Artifact;
36 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
37 import org.apache.maven.doxia.sink.Sink;
38 import org.apache.maven.doxia.sink.SinkEventAttributes;
39 import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
40 import org.apache.maven.model.Dependency;
41 import org.apache.maven.plugins.annotations.Component;
42 import org.apache.maven.plugins.annotations.Mojo;
43 import org.apache.maven.project.DefaultProjectBuildingRequest;
44 import org.apache.maven.project.MavenProject;
45 import org.apache.maven.project.ProjectBuildingRequest;
46 import org.apache.maven.report.projectinfo.dependencies.DependencyVersionMap;
47 import org.apache.maven.report.projectinfo.dependencies.SinkSerializingDependencyNodeVisitor;
48 import org.apache.maven.reporting.MavenReportException;
49 import org.apache.maven.shared.artifact.filter.StrictPatternIncludesArtifactFilter;
50 import org.apache.maven.shared.dependency.graph.DependencyCollectorBuilder;
51 import org.apache.maven.shared.dependency.graph.DependencyCollectorBuilderException;
52 import org.apache.maven.shared.dependency.graph.DependencyNode;
53 import org.apache.maven.shared.dependency.graph.filter.AncestorOrSelfDependencyNodeFilter;
54 import org.apache.maven.shared.dependency.graph.filter.AndDependencyNodeFilter;
55 import org.apache.maven.shared.dependency.graph.filter.ArtifactDependencyNodeFilter;
56 import org.apache.maven.shared.dependency.graph.filter.DependencyNodeFilter;
57 import org.apache.maven.shared.dependency.graph.traversal.BuildingDependencyNodeVisitor;
58 import org.apache.maven.shared.dependency.graph.traversal.CollectingDependencyNodeVisitor;
59 import org.apache.maven.shared.dependency.graph.traversal.DependencyNodeVisitor;
60 import org.apache.maven.shared.dependency.graph.traversal.FilteringDependencyNodeVisitor;
61
62
63
64
65
66
67
68
69
70 @Mojo( name = "dependency-convergence", aggregator = true )
71 public class DependencyConvergenceReport
72 extends AbstractProjectInfoReport
73 {
74
75
76
77 private static final String IMG_SUCCESS_URL = "images/icon_success_sml.gif";
78
79
80
81
82 private static final String IMG_ERROR_URL = "images/icon_error_sml.gif";
83
84 private static final int FULL_CONVERGENCE = 100;
85
86
87
88
89
90
91
92
93 @Component
94 private DependencyCollectorBuilder dependencyCollectorBuilder;
95
96 private ArtifactFilter filter = null;
97
98 private Map<MavenProject, DependencyNode> projectMap = new HashMap<>();
99
100
101
102
103
104
105
106
107 public String getOutputName()
108 {
109 return "dependency-convergence";
110 }
111
112 @Override
113 protected String getI18Nsection()
114 {
115 return "dependency-convergence";
116 }
117
118
119
120
121
122 @Override
123 protected void executeReport( Locale locale )
124 throws MavenReportException
125 {
126 Sink sink = getSink();
127
128 sink.head();
129 sink.title();
130
131 if ( isReactorBuild() )
132 {
133 sink.text( getI18nString( locale, "reactor.title" ) );
134 }
135 else
136 {
137 sink.text( getI18nString( locale, "title" ) );
138 }
139
140 sink.title_();
141 sink.head_();
142
143 sink.body();
144
145 sink.section1();
146
147 sink.sectionTitle1();
148
149 if ( isReactorBuild() )
150 {
151 sink.text( getI18nString( locale, "reactor.title" ) );
152 }
153 else
154 {
155 sink.text( getI18nString( locale, "title" ) );
156 }
157
158 sink.sectionTitle1_();
159
160 DependencyAnalyzeResult dependencyResult = analyzeDependencyTree();
161 int convergence = calculateConvergence( dependencyResult );
162
163 if ( convergence < FULL_CONVERGENCE )
164 {
165
166 generateLegend( locale, sink );
167 sink.lineBreak();
168 }
169
170
171 generateStats( locale, sink, dependencyResult );
172
173 sink.section1_();
174
175 if ( convergence < FULL_CONVERGENCE )
176 {
177
178 generateConvergence( locale, sink, dependencyResult );
179 }
180
181 sink.body_();
182 sink.flush();
183 sink.close();
184 }
185
186
187
188
189
190
191
192
193
194
195
196 private List<ReverseDependencyLink> getSnapshotDependencies(
197 Map<String, List<ReverseDependencyLink>> dependencyMap )
198 {
199 List<ReverseDependencyLink> snapshots = new ArrayList<>();
200 for ( Map.Entry<String, List<ReverseDependencyLink>> entry : dependencyMap.entrySet() )
201 {
202 List<ReverseDependencyLink> depList = entry.getValue();
203 Map<String, List<ReverseDependencyLink>> artifactMap = getSortedUniqueArtifactMap( depList );
204 for ( Map.Entry<String, List<ReverseDependencyLink>> artEntry : artifactMap.entrySet() )
205 {
206 String version = artEntry.getKey();
207 boolean isReactorProject = false;
208
209 Iterator<ReverseDependencyLink> iterator = artEntry.getValue().iterator();
210
211
212
213 ReverseDependencyLink rdl = null;
214 if ( iterator.hasNext() )
215 {
216 rdl = iterator.next();
217 if ( isReactorProject( rdl.getDependency() ) )
218 {
219 isReactorProject = true;
220 }
221 }
222
223 if ( version.endsWith( "-SNAPSHOT" ) && !isReactorProject && rdl != null )
224 {
225 snapshots.add( rdl );
226 }
227 }
228 }
229
230 return snapshots;
231 }
232
233
234
235
236
237
238
239
240 private void generateConvergence( Locale locale, Sink sink, DependencyAnalyzeResult result )
241 {
242 sink.section2();
243
244 sink.sectionTitle2();
245
246 if ( isReactorBuild() )
247 {
248 sink.text( getI18nString( locale, "convergence.caption" ) );
249 }
250 else
251 {
252 sink.text( getI18nString( locale, "convergence.single.caption" ) );
253 }
254
255 sink.sectionTitle2_();
256
257
258 for ( Map.Entry<String, List<ReverseDependencyLink>> entry : result.getConflicting().entrySet() )
259 {
260 String key = entry.getKey();
261 List<ReverseDependencyLink> depList = entry.getValue();
262
263 sink.section3();
264 sink.sectionTitle3();
265 sink.text( key );
266 sink.sectionTitle3_();
267
268 generateDependencyDetails( locale, sink, depList );
269
270 sink.section3_();
271 }
272
273
274 for ( ReverseDependencyLink dependencyLink : result.getSnapshots() )
275 {
276 sink.section3();
277 sink.sectionTitle3();
278
279 Dependency dep = dependencyLink.getDependency();
280
281 sink.text( dep.getGroupId() + ":" + dep.getArtifactId() );
282 sink.sectionTitle3_();
283
284 List<ReverseDependencyLink> depList = new ArrayList<>();
285 depList.add( dependencyLink );
286 generateDependencyDetails( locale, sink, depList );
287
288 sink.section3_();
289 }
290
291 sink.section2_();
292 }
293
294
295
296
297
298
299
300 private void generateDependencyDetails( Locale locale, Sink sink, List<ReverseDependencyLink> depList )
301 {
302 sink.table();
303
304 Map<String, List<ReverseDependencyLink>> artifactMap = getSortedUniqueArtifactMap( depList );
305
306 sink.tableRow();
307
308 sink.tableCell();
309
310 iconError( locale, sink );
311
312 sink.tableCell_();
313
314 sink.tableCell();
315
316 sink.table();
317
318 for ( String version : artifactMap.keySet() )
319 {
320 sink.tableRow();
321 sink.tableCell( new SinkEventAttributeSet( SinkEventAttributes.WIDTH, "25%" ) );
322 sink.text( version );
323 sink.tableCell_();
324
325 sink.tableCell();
326 generateVersionDetails( sink, artifactMap, version );
327 sink.tableCell_();
328
329 sink.tableRow_();
330 }
331 sink.table_();
332 sink.tableCell_();
333
334 sink.tableRow_();
335
336 sink.table_();
337 }
338
339
340
341
342
343
344
345
346 private void generateVersionDetails( Sink sink, Map<String, List<ReverseDependencyLink>> artifactMap,
347 String version )
348 {
349 sink.numberedList( 0 );
350 List<ReverseDependencyLink> depList = artifactMap.get( version );
351
352 List<DependencyNode> projectNodes = getProjectNodes( depList );
353
354 if ( projectNodes.isEmpty() )
355 {
356 getLog().warn( "Can't find project nodes for dependency list: " + depList.get( 0 ).getDependency() );
357 return;
358 }
359 Collections.sort( projectNodes, new DependencyNodeComparator() );
360
361 for ( DependencyNode projectNode : projectNodes )
362 {
363 if ( isReactorBuild() )
364 {
365 sink.numberedListItem();
366 }
367
368 showVersionDetails( projectNode, depList, sink );
369
370 if ( isReactorBuild() )
371 {
372 sink.numberedListItem_();
373 }
374
375 sink.lineBreak();
376 }
377
378 sink.numberedList_();
379 }
380
381 private List<DependencyNode> getProjectNodes( List<ReverseDependencyLink> depList )
382 {
383 List<DependencyNode> projectNodes = new ArrayList<>();
384
385 for ( ReverseDependencyLink depLink : depList )
386 {
387 MavenProject project = depLink.getProject();
388 DependencyNode projectNode = this.projectMap.get( project );
389
390 if ( projectNode != null && !projectNodes.contains( projectNode ) )
391 {
392 projectNodes.add( projectNode );
393 }
394 }
395 return projectNodes;
396 }
397
398 private void showVersionDetails( DependencyNode projectNode, List<ReverseDependencyLink> depList, Sink sink )
399 {
400 if ( depList == null || depList.isEmpty() )
401 {
402 return;
403 }
404
405 Dependency dependency = depList.get( 0 ).getDependency();
406 String key =
407 dependency.getGroupId() + ":" + dependency.getArtifactId() + ":" + dependency.getType() + ":"
408 + dependency.getVersion();
409
410 serializeDependencyTree( projectNode, key, sink );
411
412 }
413
414
415
416
417
418
419
420 private void serializeDependencyTree( DependencyNode rootNode, String key, Sink sink )
421 {
422 DependencyNodeVisitor visitor = getSerializingDependencyNodeVisitor( sink );
423
424 visitor = new BuildingDependencyNodeVisitor( visitor );
425
426 DependencyNodeFilter nodeFilter = createDependencyNodeFilter( key );
427
428 if ( nodeFilter != null )
429 {
430 CollectingDependencyNodeVisitor collectingVisitor = new CollectingDependencyNodeVisitor();
431 DependencyNodeVisitor firstPassVisitor = new FilteringDependencyNodeVisitor(
432 collectingVisitor, nodeFilter );
433 rootNode.accept( firstPassVisitor );
434
435 DependencyNodeFilter secondPassFilter =
436 new AncestorOrSelfDependencyNodeFilter( collectingVisitor.getNodes() );
437 visitor = new FilteringDependencyNodeVisitor( visitor, secondPassFilter );
438 }
439
440 rootNode.accept( visitor );
441 }
442
443
444
445
446
447
448 private DependencyNodeFilter createDependencyNodeFilter( String includes )
449 {
450 List<DependencyNodeFilter> filters = new ArrayList<>();
451
452
453 if ( includes != null )
454 {
455 List<String> patterns = Arrays.asList( includes.split( "," ) );
456
457 getLog().debug( "+ Filtering dependency tree by artifact include patterns: " + patterns );
458
459 ArtifactFilter artifactFilter = new StrictPatternIncludesArtifactFilter( patterns );
460 filters.add( new ArtifactDependencyNodeFilter( artifactFilter ) );
461 }
462
463 return filters.isEmpty() ? null : new AndDependencyNodeFilter( filters );
464 }
465
466
467
468
469
470 public DependencyNodeVisitor getSerializingDependencyNodeVisitor( Sink sink )
471 {
472 return new SinkSerializingDependencyNodeVisitor( sink );
473 }
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493 private Map<String, List<ReverseDependencyLink>> getSortedUniqueArtifactMap( List<ReverseDependencyLink> depList )
494 {
495 Map<String, List<ReverseDependencyLink>> uniqueArtifactMap = new TreeMap<>();
496
497 for ( ReverseDependencyLink rdl : depList )
498 {
499 String key = rdl.getDependency().getVersion();
500 List<ReverseDependencyLink> projectList = uniqueArtifactMap.get( key );
501 if ( projectList == null )
502 {
503 projectList = new ArrayList<>();
504 }
505 projectList.add( rdl );
506 uniqueArtifactMap.put( key, projectList );
507 }
508
509 return uniqueArtifactMap;
510 }
511
512
513
514
515
516
517
518 private void generateLegend( Locale locale, Sink sink )
519 {
520 sink.table();
521 sink.tableCaption();
522 sink.bold();
523 sink.text( getI18nString( locale, "legend" ) );
524 sink.bold_();
525 sink.tableCaption_();
526
527 sink.tableRow();
528
529 sink.tableCell();
530 iconError( locale, sink );
531 sink.tableCell_();
532 sink.tableCell();
533 sink.text( getI18nString( locale, "legend.different" ) );
534 sink.tableCell_();
535
536 sink.tableRow_();
537
538 sink.table_();
539 }
540
541
542
543
544
545
546
547
548 private void generateStats( Locale locale, Sink sink, DependencyAnalyzeResult result )
549 {
550 int depCount = result.getDependencyCount();
551
552 int artifactCount = result.getArtifactCount();
553 int snapshotCount = result.getSnapshotCount();
554 int conflictingCount = result.getConflictingCount();
555
556 int convergence = calculateConvergence( result );
557
558
559 sink.table();
560 sink.tableCaption();
561 sink.bold();
562 sink.text( getI18nString( locale, "stats.caption" ) );
563 sink.bold_();
564 sink.tableCaption_();
565
566 if ( isReactorBuild() )
567 {
568 sink.tableRow();
569 sink.tableHeaderCell();
570 sink.text( getI18nString( locale, "stats.modules" ) );
571 sink.tableHeaderCell_();
572 sink.tableCell();
573 sink.text( String.valueOf( reactorProjects.size() ) );
574 sink.tableCell_();
575 sink.tableRow_();
576 }
577
578 sink.tableRow();
579 sink.tableHeaderCell();
580 sink.text( getI18nString( locale, "stats.dependencies" ) );
581 sink.tableHeaderCell_();
582 sink.tableCell();
583 sink.text( String.valueOf( depCount ) );
584 sink.tableCell_();
585 sink.tableRow_();
586
587 sink.tableRow();
588 sink.tableHeaderCell();
589 sink.text( getI18nString( locale, "stats.artifacts" ) );
590 sink.tableHeaderCell_();
591 sink.tableCell();
592 sink.text( String.valueOf( artifactCount ) );
593 sink.tableCell_();
594 sink.tableRow_();
595
596 sink.tableRow();
597 sink.tableHeaderCell();
598 sink.text( getI18nString( locale, "stats.conflicting" ) );
599 sink.tableHeaderCell_();
600 sink.tableCell();
601 sink.text( String.valueOf( conflictingCount ) );
602 sink.tableCell_();
603 sink.tableRow_();
604
605 sink.tableRow();
606 sink.tableHeaderCell();
607 sink.text( getI18nString( locale, "stats.snapshots" ) );
608 sink.tableHeaderCell_();
609 sink.tableCell();
610 sink.text( String.valueOf( snapshotCount ) );
611 sink.tableCell_();
612 sink.tableRow_();
613
614 sink.tableRow();
615 sink.tableHeaderCell();
616 sink.text( getI18nString( locale, "stats.convergence" ) );
617 sink.tableHeaderCell_();
618 sink.tableCell();
619 if ( convergence < FULL_CONVERGENCE )
620 {
621 iconError( locale, sink );
622 }
623 else
624 {
625 iconSuccess( locale, sink );
626 }
627 sink.nonBreakingSpace();
628 sink.bold();
629 sink.text( String.valueOf( convergence ) + " %" );
630 sink.bold_();
631 sink.tableCell_();
632 sink.tableRow_();
633
634 sink.tableRow();
635 sink.tableHeaderCell();
636 sink.text( getI18nString( locale, "stats.readyrelease" ) );
637 sink.tableHeaderCell_();
638 sink.tableCell();
639 if ( convergence >= FULL_CONVERGENCE && snapshotCount <= 0 )
640 {
641 iconSuccess( locale, sink );
642 sink.nonBreakingSpace();
643 sink.bold();
644 sink.text( getI18nString( locale, "stats.readyrelease.success" ) );
645 sink.bold_();
646 }
647 else
648 {
649 iconError( locale, sink );
650 sink.nonBreakingSpace();
651 sink.bold();
652 sink.text( getI18nString( locale, "stats.readyrelease.error" ) );
653 sink.bold_();
654 if ( convergence < FULL_CONVERGENCE )
655 {
656 sink.lineBreak();
657 sink.text( getI18nString( locale, "stats.readyrelease.error.convergence" ) );
658 }
659 if ( snapshotCount > 0 )
660 {
661 sink.lineBreak();
662 sink.text( getI18nString( locale, "stats.readyrelease.error.snapshots" ) );
663 }
664 }
665 sink.tableCell_();
666 sink.tableRow_();
667
668 sink.table_();
669 }
670
671
672
673
674
675
676
677 private boolean isReactorProject( Dependency dependency )
678 {
679 for ( MavenProject mavenProject : reactorProjects )
680 {
681 if ( mavenProject.getGroupId().equals( dependency.getGroupId() )
682 && mavenProject.getArtifactId().equals( dependency.getArtifactId() ) )
683 {
684 if ( getLog().isDebugEnabled() )
685 {
686 getLog().debug( dependency + " is a reactor project" );
687 }
688 return true;
689 }
690 }
691 return false;
692 }
693
694 private boolean isReactorBuild()
695 {
696 return this.reactorProjects.size() > 1;
697 }
698
699 private void iconSuccess( Locale locale, Sink sink )
700 {
701 SinkEventAttributes attributes =
702 new SinkEventAttributeSet( SinkEventAttributes.ALT, getI18nString( locale, "icon.success" ) );
703 sink.figureGraphics( IMG_SUCCESS_URL, attributes );
704 }
705
706 private void iconError( Locale locale, Sink sink )
707 {
708 SinkEventAttributes attributes =
709 new SinkEventAttributeSet( SinkEventAttributes.ALT, getI18nString( locale, "icon.error" ) );
710 sink.figureGraphics( IMG_ERROR_URL, attributes );
711 }
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734 private DependencyAnalyzeResult analyzeDependencyTree()
735 throws MavenReportException
736 {
737 Map<String, List<ReverseDependencyLink>> conflictingDependencyMap = new TreeMap<>();
738 Map<String, List<ReverseDependencyLink>> allDependencies = new TreeMap<>();
739
740 ProjectBuildingRequest buildingRequest =
741 new DefaultProjectBuildingRequest( getSession().getProjectBuildingRequest() );
742
743 for ( MavenProject reactorProject : reactorProjects )
744 {
745 buildingRequest.setProject( reactorProject );
746
747 DependencyNode node = getNode( buildingRequest );
748
749 this.projectMap.put( reactorProject, node );
750
751 getConflictingDependencyMap( conflictingDependencyMap, reactorProject, node );
752
753 getAllDependencyMap( allDependencies, reactorProject, node );
754 }
755
756 return populateDependencyAnalyzeResult( conflictingDependencyMap, allDependencies );
757 }
758
759
760
761
762
763
764
765
766
767 private DependencyAnalyzeResult populateDependencyAnalyzeResult(
768 Map<String, List<ReverseDependencyLink>> conflictingDependencyMap,
769 Map<String, List<ReverseDependencyLink>> allDependencies )
770 {
771 DependencyAnalyzeResult dependencyResult = new DependencyAnalyzeResult();
772
773 dependencyResult.setAll( allDependencies );
774 dependencyResult.setConflicting( conflictingDependencyMap );
775
776 List<ReverseDependencyLink> snapshots = getSnapshotDependencies( allDependencies );
777 dependencyResult.setSnapshots( snapshots );
778 return dependencyResult;
779 }
780
781
782
783
784
785
786
787
788 private void getConflictingDependencyMap( Map<String, List<ReverseDependencyLink>> conflictingDependencyMap,
789 MavenProject reactorProject, DependencyNode node )
790 {
791 DependencyVersionMap visitor = new DependencyVersionMap();
792 visitor.setUniqueVersions( true );
793
794 node.accept( visitor );
795
796 for ( List<DependencyNode> nodes : visitor.getConflictedVersionNumbers() )
797 {
798 DependencyNode dependencyNode = nodes.get( 0 );
799
800 String key = dependencyNode.getArtifact().getGroupId() + ":" + dependencyNode.getArtifact().getArtifactId();
801
802 List<ReverseDependencyLink> dependencyList = conflictingDependencyMap.get( key );
803 if ( dependencyList == null )
804 {
805 dependencyList = new ArrayList<>();
806 }
807
808 dependencyList.add( new ReverseDependencyLink(
809 toDependency( dependencyNode.getArtifact() ), reactorProject ) );
810
811 for ( DependencyNode workNode : nodes.subList( 1, nodes.size() ) )
812 {
813 dependencyList.add( new ReverseDependencyLink(
814 toDependency( workNode.getArtifact() ), reactorProject ) );
815 }
816
817 conflictingDependencyMap.put( key, dependencyList );
818 }
819 }
820
821
822
823
824
825
826
827
828 private void getAllDependencyMap( Map<String, List<ReverseDependencyLink>> allDependencies,
829 MavenProject reactorProject, DependencyNode node )
830 {
831 Set<Artifact> artifacts = getAllDescendants( node );
832
833 for ( Artifact art : artifacts )
834 {
835 String key = art.getGroupId() + ":" + art.getArtifactId();
836
837 List<ReverseDependencyLink> reverseDepependencies = allDependencies.get( key );
838 if ( reverseDepependencies == null )
839 {
840 reverseDepependencies = new ArrayList<>();
841 }
842
843 if ( !containsDependency( reverseDepependencies, art ) )
844 {
845 reverseDepependencies.add( new ReverseDependencyLink( toDependency( art ), reactorProject ) );
846 }
847
848 allDependencies.put( key, reverseDepependencies );
849 }
850 }
851
852
853
854
855
856
857
858 private Dependency toDependency( Artifact artifact )
859 {
860 Dependency dependency = new Dependency();
861 dependency.setGroupId( artifact.getGroupId() );
862 dependency.setArtifactId( artifact.getArtifactId() );
863 dependency.setVersion( artifact.getVersion() );
864 dependency.setClassifier( artifact.getClassifier() );
865 dependency.setScope( artifact.getScope() );
866
867 return dependency;
868 }
869
870
871
872
873
874
875
876
877 private boolean containsDependency( List<ReverseDependencyLink> reverseDependencies, Artifact art )
878 {
879
880 for ( ReverseDependencyLink revDependency : reverseDependencies )
881 {
882 Dependency dep = revDependency.getDependency();
883 if ( dep.getGroupId().equals( art.getGroupId() ) && dep.getArtifactId().equals( art.getArtifactId() )
884 && dep.getVersion().equals( art.getVersion() ) )
885 {
886 return true;
887 }
888 }
889
890 return false;
891 }
892
893
894
895
896
897
898
899
900 private DependencyNode getNode( ProjectBuildingRequest buildingRequest )
901 throws MavenReportException
902 {
903 try
904 {
905 return dependencyCollectorBuilder.collectDependencyGraph( buildingRequest, filter );
906 }
907 catch ( DependencyCollectorBuilderException e )
908 {
909 throw new MavenReportException( "Could not build dependency tree: " + e.getMessage(), e );
910 }
911 }
912
913
914
915
916
917
918
919 private Set<Artifact> getAllDescendants( DependencyNode node )
920 {
921 Set<Artifact> children = null;
922 if ( node.getChildren() != null )
923 {
924 children = new HashSet<>();
925 for ( DependencyNode depNode : node.getChildren() )
926 {
927 children.add( depNode.getArtifact() );
928 Set<Artifact> subNodes = getAllDescendants( depNode );
929 if ( subNodes != null )
930 {
931 children.addAll( subNodes );
932 }
933 }
934 }
935 return children;
936 }
937
938 private int calculateConvergence( DependencyAnalyzeResult result )
939 {
940 return (int) ( ( (double) result.getDependencyCount()
941 / (double) result.getArtifactCount() ) * FULL_CONVERGENCE );
942 }
943
944
945
946
947 private static class ReverseDependencyLink
948 {
949 private Dependency dependency;
950
951 protected MavenProject project;
952
953 ReverseDependencyLink( Dependency dependency, MavenProject project )
954 {
955 this.dependency = dependency;
956 this.project = project;
957 }
958
959 public Dependency getDependency()
960 {
961 return dependency;
962 }
963
964 public MavenProject getProject()
965 {
966 return project;
967 }
968
969 @Override
970 public String toString()
971 {
972 return project.getId();
973 }
974 }
975
976
977
978
979 static class DependencyNodeComparator
980 implements Comparator<DependencyNode>
981 {
982
983
984
985 public int compare( DependencyNode p1, DependencyNode p2 )
986 {
987 return p1.getArtifact().getId().compareTo( p2.getArtifact().getId() );
988 }
989 }
990
991
992
993
994 private class DependencyAnalyzeResult
995 {
996 Map<String, List<ReverseDependencyLink>> all;
997
998 List<ReverseDependencyLink> snapshots;
999
1000 Map<String, List<ReverseDependencyLink>> conflicting;
1001
1002 public void setAll( Map<String, List<ReverseDependencyLink>> all )
1003 {
1004 this.all = all;
1005 }
1006
1007 public List<ReverseDependencyLink> getSnapshots()
1008 {
1009 return snapshots;
1010 }
1011
1012 public void setSnapshots( List<ReverseDependencyLink> snapshots )
1013 {
1014 this.snapshots = snapshots;
1015 }
1016
1017 public Map<String, List<ReverseDependencyLink>> getConflicting()
1018 {
1019 return conflicting;
1020 }
1021
1022 public void setConflicting( Map<String, List<ReverseDependencyLink>> conflicting )
1023 {
1024 this.conflicting = conflicting;
1025 }
1026
1027 public int getDependencyCount()
1028 {
1029 return all.size();
1030 }
1031
1032 public int getSnapshotCount()
1033 {
1034 return this.snapshots.size();
1035 }
1036
1037 public int getConflictingCount()
1038 {
1039 return this.conflicting.size();
1040 }
1041
1042 public int getArtifactCount()
1043 {
1044 int artifactCount = 0;
1045 for ( List<ReverseDependencyLink> depList : this.all.values() )
1046 {
1047 Map<String, List<ReverseDependencyLink>> artifactMap = getSortedUniqueArtifactMap( depList );
1048 artifactCount += artifactMap.size();
1049 }
1050
1051 return artifactCount;
1052 }
1053 }
1054
1055 }