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