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