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