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.tree.DependencyTreeBuilder;
51 import org.apache.maven.shared.dependency.tree.DependencyNode;
52 import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;
53 import org.apache.maven.shared.dependency.tree.filter.AncestorOrSelfDependencyNodeFilter;
54 import org.apache.maven.shared.dependency.tree.filter.AndDependencyNodeFilter;
55 import org.apache.maven.shared.dependency.tree.filter.ArtifactDependencyNodeFilter;
56 import org.apache.maven.shared.dependency.tree.filter.DependencyNodeFilter;
57 import org.apache.maven.shared.dependency.tree.traversal.BuildingDependencyNodeVisitor;
58 import org.apache.maven.shared.dependency.tree.traversal.CollectingDependencyNodeVisitor;
59 import org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor;
60 import org.apache.maven.shared.dependency.tree.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 private static final String IMG_SUCCESS_URL = "images/icon_success_sml.gif";
76
77
78 private static final String IMG_ERROR_URL = "images/icon_error_sml.gif";
79
80 private static final int FULL_CONVERGENCE = 100;
81
82
83
84
85
86
87
88
89 @Component
90 private DependencyTreeBuilder dependencyTreeBuilder;
91
92 private ArtifactFilter filter = null;
93
94 private Map<MavenProject, DependencyNode> projectMap = new HashMap<>();
95
96
97
98
99
100
101 public String getOutputName()
102 {
103 return "dependency-convergence";
104 }
105
106 @Override
107 protected String getI18Nsection()
108 {
109 return "dependency-convergence";
110 }
111
112
113
114
115
116 @Override
117 protected void executeReport( Locale locale )
118 throws MavenReportException
119 {
120 Sink sink = getSink();
121
122 sink.head();
123 sink.title();
124
125 if ( isReactorBuild() )
126 {
127 sink.text( getI18nString( locale, "reactor.title" ) );
128 }
129 else
130 {
131 sink.text( getI18nString( locale, "title" ) );
132 }
133
134 sink.title_();
135 sink.head_();
136
137 sink.body();
138
139 sink.section1();
140
141 sink.sectionTitle1();
142
143 if ( isReactorBuild() )
144 {
145 sink.text( getI18nString( locale, "reactor.title" ) );
146 }
147 else
148 {
149 sink.text( getI18nString( locale, "title" ) );
150 }
151
152 sink.sectionTitle1_();
153
154 DependencyAnalyzeResult dependencyResult = analyzeDependencyTree();
155 int convergence = calculateConvergence( dependencyResult );
156
157 if ( convergence < FULL_CONVERGENCE )
158 {
159
160 generateLegend( locale, sink );
161 sink.lineBreak();
162 }
163
164
165 generateStats( locale, sink, dependencyResult );
166
167 sink.section1_();
168
169 if ( convergence < FULL_CONVERGENCE )
170 {
171
172 generateConvergence( locale, sink, dependencyResult );
173 }
174
175 sink.body_();
176 sink.flush();
177 sink.close();
178 }
179
180
181
182
183
184
185
186
187
188
189
190 private List<ReverseDependencyLink> getSnapshotDependencies(
191 Map<String, List<ReverseDependencyLink>> dependencyMap )
192 {
193 List<ReverseDependencyLink> snapshots = new ArrayList<>();
194 for ( Map.Entry<String, List<ReverseDependencyLink>> entry : dependencyMap.entrySet() )
195 {
196 List<ReverseDependencyLink> depList = entry.getValue();
197 Map<String, List<ReverseDependencyLink>> artifactMap = getSortedUniqueArtifactMap( depList );
198 for ( Map.Entry<String, List<ReverseDependencyLink>> artEntry : artifactMap.entrySet() )
199 {
200 String version = artEntry.getKey();
201 boolean isReactorProject = false;
202
203 Iterator<ReverseDependencyLink> iterator = artEntry.getValue().iterator();
204
205
206
207 ReverseDependencyLink rdl = null;
208 if ( iterator.hasNext() )
209 {
210 rdl = iterator.next();
211 if ( isReactorProject( rdl.getDependency() ) )
212 {
213 isReactorProject = true;
214 }
215 }
216
217 if ( version.endsWith( "-SNAPSHOT" ) && !isReactorProject && rdl != null )
218 {
219 snapshots.add( rdl );
220 }
221 }
222 }
223
224 return snapshots;
225 }
226
227
228
229
230
231
232
233
234 private void generateConvergence( Locale locale, Sink sink, DependencyAnalyzeResult result )
235 {
236 sink.section2();
237
238 sink.sectionTitle2();
239
240 if ( isReactorBuild() )
241 {
242 sink.text( getI18nString( locale, "convergence.caption" ) );
243 }
244 else
245 {
246 sink.text( getI18nString( locale, "convergence.single.caption" ) );
247 }
248
249 sink.sectionTitle2_();
250
251
252 for ( Map.Entry<String, List<ReverseDependencyLink>> entry : result.getConflicting().entrySet() )
253 {
254 String key = entry.getKey();
255 List<ReverseDependencyLink> depList = entry.getValue();
256
257 sink.section3();
258 sink.sectionTitle3();
259 sink.text( key );
260 sink.sectionTitle3_();
261
262 generateDependencyDetails( locale, sink, depList );
263
264 sink.section3_();
265 }
266
267
268 for ( ReverseDependencyLink dependencyLink : result.getSnapshots() )
269 {
270 sink.section3();
271 sink.sectionTitle3();
272
273 Dependency dep = dependencyLink.getDependency();
274
275 sink.text( dep.getGroupId() + ":" + dep.getArtifactId() );
276 sink.sectionTitle3_();
277
278 List<ReverseDependencyLink> depList = new ArrayList<>();
279 depList.add( dependencyLink );
280 generateDependencyDetails( locale, sink, depList );
281
282 sink.section3_();
283 }
284
285 sink.section2_();
286 }
287
288
289
290
291
292
293
294 private void generateDependencyDetails( Locale locale, Sink sink, List<ReverseDependencyLink> depList )
295 {
296 sink.table();
297
298 Map<String, List<ReverseDependencyLink>> artifactMap = getSortedUniqueArtifactMap( depList );
299
300 sink.tableRow();
301
302 sink.tableCell();
303
304 iconError( locale, sink );
305
306 sink.tableCell_();
307
308 sink.tableCell();
309
310 sink.table();
311
312 for ( String version : artifactMap.keySet() )
313 {
314 sink.tableRow();
315 sink.tableCell( new SinkEventAttributeSet( SinkEventAttributes.WIDTH, "25%" ) );
316 sink.text( version );
317 sink.tableCell_();
318
319 sink.tableCell();
320 generateVersionDetails( sink, artifactMap, version );
321 sink.tableCell_();
322
323 sink.tableRow_();
324 }
325 sink.table_();
326 sink.tableCell_();
327
328 sink.tableRow_();
329
330 sink.table_();
331 }
332
333
334
335
336
337
338
339
340 private void generateVersionDetails( Sink sink, Map<String, List<ReverseDependencyLink>> artifactMap,
341 String version )
342 {
343 sink.numberedList( 0 );
344 List<ReverseDependencyLink> depList = artifactMap.get( version );
345
346 List<DependencyNode> projectNodes = getProjectNodes( depList );
347
348 if ( projectNodes.isEmpty() )
349 {
350 getLog().warn( "Can't find project nodes for dependency list: " + depList.get( 0 ).getDependency() );
351 return;
352 }
353 Collections.sort( projectNodes, new DependencyNodeComparator() );
354
355 for ( DependencyNode projectNode : projectNodes )
356 {
357 if ( isReactorBuild() )
358 {
359 sink.numberedListItem();
360 }
361
362 showVersionDetails( projectNode, depList, sink );
363
364 if ( isReactorBuild() )
365 {
366 sink.numberedListItem_();
367 }
368
369 sink.lineBreak();
370 }
371
372 sink.numberedList_();
373 }
374
375 private List<DependencyNode> getProjectNodes( List<ReverseDependencyLink> depList )
376 {
377 List<DependencyNode> projectNodes = new ArrayList<>();
378
379 for ( ReverseDependencyLink depLink : depList )
380 {
381 MavenProject project = depLink.getProject();
382 DependencyNode projectNode = this.projectMap.get( project );
383
384 if ( projectNode != null && !projectNodes.contains( projectNode ) )
385 {
386 projectNodes.add( projectNode );
387 }
388 }
389 return projectNodes;
390 }
391
392 private void showVersionDetails( DependencyNode projectNode, List<ReverseDependencyLink> depList, Sink sink )
393 {
394 if ( depList == null || depList.isEmpty() )
395 {
396 return;
397 }
398
399 Dependency dependency = depList.get( 0 ).getDependency();
400 String key =
401 dependency.getGroupId() + ":" + dependency.getArtifactId() + ":" + dependency.getType() + ":"
402 + dependency.getVersion();
403
404 serializeDependencyTree( projectNode, key, sink );
405
406 }
407
408
409
410
411
412
413
414 private void serializeDependencyTree( DependencyNode rootNode, String key, Sink sink )
415 {
416 DependencyNodeVisitor visitor = getSerializingDependencyNodeVisitor( sink );
417
418 visitor = new BuildingDependencyNodeVisitor( visitor );
419
420 DependencyNodeFilter nodeFilter = createDependencyNodeFilter( key );
421
422 if ( nodeFilter != null )
423 {
424 CollectingDependencyNodeVisitor collectingVisitor = new CollectingDependencyNodeVisitor();
425 DependencyNodeVisitor firstPassVisitor = new FilteringDependencyNodeVisitor(
426 collectingVisitor, nodeFilter );
427 rootNode.accept( firstPassVisitor );
428
429 DependencyNodeFilter secondPassFilter =
430 new AncestorOrSelfDependencyNodeFilter( collectingVisitor.getNodes() );
431 visitor = new FilteringDependencyNodeVisitor( visitor, secondPassFilter );
432 }
433
434 rootNode.accept( visitor );
435 }
436
437
438
439
440
441
442 private DependencyNodeFilter createDependencyNodeFilter( String includes )
443 {
444 List<DependencyNodeFilter> filters = new ArrayList<>();
445
446
447 if ( includes != null )
448 {
449 List<String> patterns = Arrays.asList( includes.split( "," ) );
450
451 getLog().debug( "+ Filtering dependency tree by artifact include patterns: " + patterns );
452
453 ArtifactFilter artifactFilter = new StrictPatternIncludesArtifactFilter( patterns );
454 filters.add( new ArtifactDependencyNodeFilter( artifactFilter ) );
455 }
456
457 return filters.isEmpty() ? null : new AndDependencyNodeFilter( filters );
458 }
459
460
461
462
463
464 public DependencyNodeVisitor getSerializingDependencyNodeVisitor( Sink sink )
465 {
466 return new SinkSerializingDependencyNodeVisitor( sink );
467 }
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487 private Map<String, List<ReverseDependencyLink>> getSortedUniqueArtifactMap( List<ReverseDependencyLink> depList )
488 {
489 Map<String, List<ReverseDependencyLink>> uniqueArtifactMap = new TreeMap<>();
490
491 for ( ReverseDependencyLink rdl : depList )
492 {
493 String key = rdl.getDependency().getVersion();
494 List<ReverseDependencyLink> projectList = uniqueArtifactMap.get( key );
495 if ( projectList == null )
496 {
497 projectList = new ArrayList<>();
498 }
499 projectList.add( rdl );
500 uniqueArtifactMap.put( key, projectList );
501 }
502
503 return uniqueArtifactMap;
504 }
505
506
507
508
509
510
511
512 private void generateLegend( Locale locale, Sink sink )
513 {
514 sink.table();
515 sink.tableCaption();
516 sink.bold();
517 sink.text( getI18nString( locale, "legend" ) );
518 sink.bold_();
519 sink.tableCaption_();
520
521 sink.tableRow();
522
523 sink.tableCell();
524 iconError( locale, sink );
525 sink.tableCell_();
526 sink.tableCell();
527 sink.text( getI18nString( locale, "legend.different" ) );
528 sink.tableCell_();
529
530 sink.tableRow_();
531
532 sink.table_();
533 }
534
535
536
537
538
539
540
541
542 private void generateStats( Locale locale, Sink sink, DependencyAnalyzeResult result )
543 {
544 int depCount = result.getDependencyCount();
545
546 int artifactCount = result.getArtifactCount();
547 int snapshotCount = result.getSnapshotCount();
548 int conflictingCount = result.getConflictingCount();
549
550 int convergence = calculateConvergence( result );
551
552
553 sink.table();
554 sink.tableCaption();
555 sink.bold();
556 sink.text( getI18nString( locale, "stats.caption" ) );
557 sink.bold_();
558 sink.tableCaption_();
559
560 if ( isReactorBuild() )
561 {
562 sink.tableRow();
563 sink.tableHeaderCell();
564 sink.text( getI18nString( locale, "stats.modules" ) );
565 sink.tableHeaderCell_();
566 sink.tableCell();
567 sink.text( String.valueOf( reactorProjects.size() ) );
568 sink.tableCell_();
569 sink.tableRow_();
570 }
571
572 sink.tableRow();
573 sink.tableHeaderCell();
574 sink.text( getI18nString( locale, "stats.dependencies" ) );
575 sink.tableHeaderCell_();
576 sink.tableCell();
577 sink.text( String.valueOf( depCount ) );
578 sink.tableCell_();
579 sink.tableRow_();
580
581 sink.tableRow();
582 sink.tableHeaderCell();
583 sink.text( getI18nString( locale, "stats.artifacts" ) );
584 sink.tableHeaderCell_();
585 sink.tableCell();
586 sink.text( String.valueOf( artifactCount ) );
587 sink.tableCell_();
588 sink.tableRow_();
589
590 sink.tableRow();
591 sink.tableHeaderCell();
592 sink.text( getI18nString( locale, "stats.conflicting" ) );
593 sink.tableHeaderCell_();
594 sink.tableCell();
595 sink.text( String.valueOf( conflictingCount ) );
596 sink.tableCell_();
597 sink.tableRow_();
598
599 sink.tableRow();
600 sink.tableHeaderCell();
601 sink.text( getI18nString( locale, "stats.snapshots" ) );
602 sink.tableHeaderCell_();
603 sink.tableCell();
604 sink.text( String.valueOf( snapshotCount ) );
605 sink.tableCell_();
606 sink.tableRow_();
607
608 sink.tableRow();
609 sink.tableHeaderCell();
610 sink.text( getI18nString( locale, "stats.convergence" ) );
611 sink.tableHeaderCell_();
612 sink.tableCell();
613 if ( convergence < FULL_CONVERGENCE )
614 {
615 iconError( locale, sink );
616 }
617 else
618 {
619 iconSuccess( locale, sink );
620 }
621 sink.nonBreakingSpace();
622 sink.bold();
623 sink.text( String.valueOf( convergence ) + " %" );
624 sink.bold_();
625 sink.tableCell_();
626 sink.tableRow_();
627
628 sink.tableRow();
629 sink.tableHeaderCell();
630 sink.text( getI18nString( locale, "stats.readyrelease" ) );
631 sink.tableHeaderCell_();
632 sink.tableCell();
633 if ( convergence >= FULL_CONVERGENCE && snapshotCount <= 0 )
634 {
635 iconSuccess( locale, sink );
636 sink.nonBreakingSpace();
637 sink.bold();
638 sink.text( getI18nString( locale, "stats.readyrelease.success" ) );
639 sink.bold_();
640 }
641 else
642 {
643 iconError( locale, sink );
644 sink.nonBreakingSpace();
645 sink.bold();
646 sink.text( getI18nString( locale, "stats.readyrelease.error" ) );
647 sink.bold_();
648 if ( convergence < FULL_CONVERGENCE )
649 {
650 sink.lineBreak();
651 sink.text( getI18nString( locale, "stats.readyrelease.error.convergence" ) );
652 }
653 if ( snapshotCount > 0 )
654 {
655 sink.lineBreak();
656 sink.text( getI18nString( locale, "stats.readyrelease.error.snapshots" ) );
657 }
658 }
659 sink.tableCell_();
660 sink.tableRow_();
661
662 sink.table_();
663 }
664
665
666
667
668
669
670
671 private boolean isReactorProject( Dependency dependency )
672 {
673 for ( MavenProject mavenProject : reactorProjects )
674 {
675 if ( mavenProject.getGroupId().equals( dependency.getGroupId() )
676 && mavenProject.getArtifactId().equals( dependency.getArtifactId() ) )
677 {
678 if ( getLog().isDebugEnabled() )
679 {
680 getLog().debug( dependency + " is a reactor project" );
681 }
682 return true;
683 }
684 }
685 return false;
686 }
687
688 private boolean isReactorBuild()
689 {
690 return this.reactorProjects.size() > 1;
691 }
692
693 private void iconSuccess( Locale locale, Sink sink )
694 {
695 sink.figure();
696 sink.figureCaption();
697 sink.text( getI18nString( locale, "icon.success" ) );
698 sink.figureCaption_();
699 sink.figureGraphics( IMG_SUCCESS_URL );
700 sink.figure_();
701 }
702
703 private void iconError( Locale locale, Sink sink )
704 {
705 sink.figure();
706 sink.figureCaption();
707 sink.text( getI18nString( locale, "icon.error" ) );
708 sink.figureCaption_();
709 sink.figureGraphics( IMG_ERROR_URL );
710 sink.figure_();
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 dependencyTreeBuilder.buildDependencyTree( buildingRequest.getProject(), localRepository, filter );
906 }
907 catch ( DependencyTreeBuilderException 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 public int compare( DependencyNode p1, DependencyNode p2 )
984 {
985 return p1.getArtifact().getId().compareTo( p2.getArtifact().getId() );
986 }
987 }
988
989
990
991
992 private class DependencyAnalyzeResult
993 {
994 Map<String, List<ReverseDependencyLink>> all;
995
996 List<ReverseDependencyLink> snapshots;
997
998 Map<String, List<ReverseDependencyLink>> conflicting;
999
1000 public void setAll( Map<String, List<ReverseDependencyLink>> all )
1001 {
1002 this.all = all;
1003 }
1004
1005 public List<ReverseDependencyLink> getSnapshots()
1006 {
1007 return snapshots;
1008 }
1009
1010 public void setSnapshots( List<ReverseDependencyLink> snapshots )
1011 {
1012 this.snapshots = snapshots;
1013 }
1014
1015 public Map<String, List<ReverseDependencyLink>> getConflicting()
1016 {
1017 return conflicting;
1018 }
1019
1020 public void setConflicting( Map<String, List<ReverseDependencyLink>> conflicting )
1021 {
1022 this.conflicting = conflicting;
1023 }
1024
1025 public int getDependencyCount()
1026 {
1027 return all.size();
1028 }
1029
1030 public int getSnapshotCount()
1031 {
1032 return this.snapshots.size();
1033 }
1034
1035 public int getConflictingCount()
1036 {
1037 return this.conflicting.size();
1038 }
1039
1040 public int getArtifactCount()
1041 {
1042 int artifactCount = 0;
1043 for ( List<ReverseDependencyLink> depList : this.all.values() )
1044 {
1045 Map<String, List<ReverseDependencyLink>> artifactMap = getSortedUniqueArtifactMap( depList );
1046 artifactCount += artifactMap.size();
1047 }
1048
1049 return artifactCount;
1050 }
1051 }
1052
1053 }