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 SinkEventAttributes attributes =
696 new SinkEventAttributeSet( SinkEventAttributes.ALT, getI18nString( locale, "icon.success" ) );
697 sink.figureGraphics( IMG_SUCCESS_URL, attributes );
698 }
699
700 private void iconError( Locale locale, Sink sink )
701 {
702 SinkEventAttributes attributes =
703 new SinkEventAttributeSet( SinkEventAttributes.ALT, getI18nString( locale, "icon.error" ) );
704 sink.figureGraphics( IMG_ERROR_URL, attributes );
705 }
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728 private DependencyAnalyzeResult analyzeDependencyTree()
729 throws MavenReportException
730 {
731 Map<String, List<ReverseDependencyLink>> conflictingDependencyMap = new TreeMap<>();
732 Map<String, List<ReverseDependencyLink>> allDependencies = new TreeMap<>();
733
734 ProjectBuildingRequest buildingRequest =
735 new DefaultProjectBuildingRequest( getSession().getProjectBuildingRequest() );
736
737 for ( MavenProject reactorProject : reactorProjects )
738 {
739 buildingRequest.setProject( reactorProject );
740
741 DependencyNode node = getNode( buildingRequest );
742
743 this.projectMap.put( reactorProject, node );
744
745 getConflictingDependencyMap( conflictingDependencyMap, reactorProject, node );
746
747 getAllDependencyMap( allDependencies, reactorProject, node );
748 }
749
750 return populateDependencyAnalyzeResult( conflictingDependencyMap, allDependencies );
751 }
752
753
754
755
756
757
758
759
760
761 private DependencyAnalyzeResult populateDependencyAnalyzeResult(
762 Map<String, List<ReverseDependencyLink>> conflictingDependencyMap,
763 Map<String, List<ReverseDependencyLink>> allDependencies )
764 {
765 DependencyAnalyzeResult dependencyResult = new DependencyAnalyzeResult();
766
767 dependencyResult.setAll( allDependencies );
768 dependencyResult.setConflicting( conflictingDependencyMap );
769
770 List<ReverseDependencyLink> snapshots = getSnapshotDependencies( allDependencies );
771 dependencyResult.setSnapshots( snapshots );
772 return dependencyResult;
773 }
774
775
776
777
778
779
780
781
782 private void getConflictingDependencyMap( Map<String, List<ReverseDependencyLink>> conflictingDependencyMap,
783 MavenProject reactorProject, DependencyNode node )
784 {
785 DependencyVersionMap visitor = new DependencyVersionMap();
786 visitor.setUniqueVersions( true );
787
788 node.accept( visitor );
789
790 for ( List<DependencyNode> nodes : visitor.getConflictedVersionNumbers() )
791 {
792 DependencyNode dependencyNode = nodes.get( 0 );
793
794 String key = dependencyNode.getArtifact().getGroupId() + ":" + dependencyNode.getArtifact().getArtifactId();
795
796 List<ReverseDependencyLink> dependencyList = conflictingDependencyMap.get( key );
797 if ( dependencyList == null )
798 {
799 dependencyList = new ArrayList<>();
800 }
801
802 dependencyList.add( new ReverseDependencyLink(
803 toDependency( dependencyNode.getArtifact() ), reactorProject ) );
804
805 for ( DependencyNode workNode : nodes.subList( 1, nodes.size() ) )
806 {
807 dependencyList.add( new ReverseDependencyLink(
808 toDependency( workNode.getArtifact() ), reactorProject ) );
809 }
810
811 conflictingDependencyMap.put( key, dependencyList );
812 }
813 }
814
815
816
817
818
819
820
821
822 private void getAllDependencyMap( Map<String, List<ReverseDependencyLink>> allDependencies,
823 MavenProject reactorProject, DependencyNode node )
824 {
825 Set<Artifact> artifacts = getAllDescendants( node );
826
827 for ( Artifact art : artifacts )
828 {
829 String key = art.getGroupId() + ":" + art.getArtifactId();
830
831 List<ReverseDependencyLink> reverseDepependencies = allDependencies.get( key );
832 if ( reverseDepependencies == null )
833 {
834 reverseDepependencies = new ArrayList<>();
835 }
836
837 if ( !containsDependency( reverseDepependencies, art ) )
838 {
839 reverseDepependencies.add( new ReverseDependencyLink( toDependency( art ), reactorProject ) );
840 }
841
842 allDependencies.put( key, reverseDepependencies );
843 }
844 }
845
846
847
848
849
850
851
852 private Dependency toDependency( Artifact artifact )
853 {
854 Dependency dependency = new Dependency();
855 dependency.setGroupId( artifact.getGroupId() );
856 dependency.setArtifactId( artifact.getArtifactId() );
857 dependency.setVersion( artifact.getVersion() );
858 dependency.setClassifier( artifact.getClassifier() );
859 dependency.setScope( artifact.getScope() );
860
861 return dependency;
862 }
863
864
865
866
867
868
869
870
871 private boolean containsDependency( List<ReverseDependencyLink> reverseDependencies, Artifact art )
872 {
873
874 for ( ReverseDependencyLink revDependency : reverseDependencies )
875 {
876 Dependency dep = revDependency.getDependency();
877 if ( dep.getGroupId().equals( art.getGroupId() ) && dep.getArtifactId().equals( art.getArtifactId() )
878 && dep.getVersion().equals( art.getVersion() ) )
879 {
880 return true;
881 }
882 }
883
884 return false;
885 }
886
887
888
889
890
891
892
893
894 private DependencyNode getNode( ProjectBuildingRequest buildingRequest )
895 throws MavenReportException
896 {
897 try
898 {
899 return dependencyTreeBuilder.buildDependencyTree( buildingRequest.getProject(), localRepository, filter );
900 }
901 catch ( DependencyTreeBuilderException e )
902 {
903 throw new MavenReportException( "Could not build dependency tree: " + e.getMessage(), e );
904 }
905 }
906
907
908
909
910
911
912
913 private Set<Artifact> getAllDescendants( DependencyNode node )
914 {
915 Set<Artifact> children = null;
916 if ( node.getChildren() != null )
917 {
918 children = new HashSet<>();
919 for ( DependencyNode depNode : node.getChildren() )
920 {
921 children.add( depNode.getArtifact() );
922 Set<Artifact> subNodes = getAllDescendants( depNode );
923 if ( subNodes != null )
924 {
925 children.addAll( subNodes );
926 }
927 }
928 }
929 return children;
930 }
931
932 private int calculateConvergence( DependencyAnalyzeResult result )
933 {
934 return (int) ( ( (double) result.getDependencyCount()
935 / (double) result.getArtifactCount() ) * FULL_CONVERGENCE );
936 }
937
938
939
940
941 private static class ReverseDependencyLink
942 {
943 private Dependency dependency;
944
945 protected MavenProject project;
946
947 ReverseDependencyLink( Dependency dependency, MavenProject project )
948 {
949 this.dependency = dependency;
950 this.project = project;
951 }
952
953 public Dependency getDependency()
954 {
955 return dependency;
956 }
957
958 public MavenProject getProject()
959 {
960 return project;
961 }
962
963 @Override
964 public String toString()
965 {
966 return project.getId();
967 }
968 }
969
970
971
972
973 static class DependencyNodeComparator
974 implements Comparator<DependencyNode>
975 {
976
977 public int compare( DependencyNode p1, DependencyNode p2 )
978 {
979 return p1.getArtifact().getId().compareTo( p2.getArtifact().getId() );
980 }
981 }
982
983
984
985
986 private class DependencyAnalyzeResult
987 {
988 Map<String, List<ReverseDependencyLink>> all;
989
990 List<ReverseDependencyLink> snapshots;
991
992 Map<String, List<ReverseDependencyLink>> conflicting;
993
994 public void setAll( Map<String, List<ReverseDependencyLink>> all )
995 {
996 this.all = all;
997 }
998
999 public List<ReverseDependencyLink> getSnapshots()
1000 {
1001 return snapshots;
1002 }
1003
1004 public void setSnapshots( List<ReverseDependencyLink> snapshots )
1005 {
1006 this.snapshots = snapshots;
1007 }
1008
1009 public Map<String, List<ReverseDependencyLink>> getConflicting()
1010 {
1011 return conflicting;
1012 }
1013
1014 public void setConflicting( Map<String, List<ReverseDependencyLink>> conflicting )
1015 {
1016 this.conflicting = conflicting;
1017 }
1018
1019 public int getDependencyCount()
1020 {
1021 return all.size();
1022 }
1023
1024 public int getSnapshotCount()
1025 {
1026 return this.snapshots.size();
1027 }
1028
1029 public int getConflictingCount()
1030 {
1031 return this.conflicting.size();
1032 }
1033
1034 public int getArtifactCount()
1035 {
1036 int artifactCount = 0;
1037 for ( List<ReverseDependencyLink> depList : this.all.values() )
1038 {
1039 Map<String, List<ReverseDependencyLink>> artifactMap = getSortedUniqueArtifactMap( depList );
1040 artifactCount += artifactMap.size();
1041 }
1042
1043 return artifactCount;
1044 }
1045 }
1046
1047 }