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