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.dependencies.renderer;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.PrintWriter;
24 import java.io.StringWriter;
25 import java.text.DecimalFormat;
26 import java.text.DecimalFormatSymbols;
27 import java.text.FieldPosition;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.Comparator;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.Iterator;
34 import java.util.List;
35 import java.util.Locale;
36 import java.util.Map;
37 import java.util.Set;
38 import java.util.SortedSet;
39 import java.util.TreeSet;
40
41 import org.apache.maven.artifact.Artifact;
42 import org.apache.maven.doxia.sink.Sink;
43 import org.apache.maven.doxia.sink.SinkEventAttributes;
44 import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
45 import org.apache.maven.doxia.util.HtmlTools;
46 import org.apache.maven.model.License;
47 import org.apache.maven.plugin.logging.Log;
48 import org.apache.maven.project.MavenProject;
49 import org.apache.maven.project.ProjectBuilder;
50 import org.apache.maven.project.ProjectBuildingException;
51 import org.apache.maven.project.ProjectBuildingRequest;
52 import org.apache.maven.report.projectinfo.AbstractProjectInfoRenderer;
53 import org.apache.maven.report.projectinfo.LicenseMapping;
54 import org.apache.maven.report.projectinfo.ProjectInfoReportUtils;
55 import org.apache.maven.report.projectinfo.dependencies.Dependencies;
56 import org.apache.maven.report.projectinfo.dependencies.DependenciesReportConfiguration;
57 import org.apache.maven.report.projectinfo.dependencies.RepositoryUtils;
58 import org.apache.maven.repository.RepositorySystem;
59 import org.apache.maven.shared.dependency.graph.DependencyNode;
60 import org.apache.maven.shared.jar.JarData;
61 import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolverException;
62 import org.codehaus.plexus.i18n.I18N;
63 import org.codehaus.plexus.util.StringUtils;
64
65
66
67
68
69
70
71 public class DependenciesRenderer extends AbstractProjectInfoRenderer {
72
73 private static final String IMG_INFO_URL = "./images/icon_info_sml.gif";
74
75
76 private static final String IMG_CLOSE_URL = "./images/close.gif";
77
78
79 protected static final DecimalFormat DEFAULT_DECIMAL_FORMAT = new DecimalFormat("###0");
80
81 private static final Set<String> JAR_SUBTYPE;
82
83 private final DependencyNode dependencyNode;
84
85 private final Dependencies dependencies;
86
87 private final DependenciesReportConfiguration configuration;
88
89 private final Log log;
90
91 private final RepositoryUtils repoUtils;
92
93
94 private final DecimalFormat fileLengthDecimalFormat;
95
96
97
98
99 private int section;
100
101
102 private int idCounter = 0;
103
104
105
106
107 private Map<String, Object> licenseMap = new HashMap<String, Object>() {
108 private static final long serialVersionUID = 1L;
109
110
111 @Override
112 public Object put(String key, Object value) {
113
114 @SuppressWarnings("unchecked")
115 SortedSet<Object> valueList = (SortedSet<Object>) get(key);
116 if (valueList == null) {
117 valueList = new TreeSet<>();
118 }
119 valueList.add(value);
120 return super.put(key, valueList);
121 }
122 };
123
124 private final RepositorySystem repositorySystem;
125
126 private final ProjectBuilder projectBuilder;
127
128 private final ProjectBuildingRequest buildingRequest;
129
130 private final Map<String, String> licenseMappings;
131
132 static {
133 Set<String> jarSubtype = new HashSet<>();
134 jarSubtype.add("jar");
135 jarSubtype.add("war");
136 jarSubtype.add("ear");
137 jarSubtype.add("sar");
138 jarSubtype.add("rar");
139 jarSubtype.add("par");
140 jarSubtype.add("ejb");
141 JAR_SUBTYPE = Collections.unmodifiableSet(jarSubtype);
142 }
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160 public DependenciesRenderer(
161 Sink sink,
162 Locale locale,
163 I18N i18n,
164 Log log,
165 Dependencies dependencies,
166 DependencyNode dependencyTreeNode,
167 DependenciesReportConfiguration config,
168 RepositoryUtils repoUtils,
169 RepositorySystem repositorySystem,
170 ProjectBuilder projectBuilder,
171 ProjectBuildingRequest buildingRequest,
172 Map<String, String> licenseMappings) {
173 super(sink, i18n, locale);
174
175 this.log = log;
176 this.dependencies = dependencies;
177 this.dependencyNode = dependencyTreeNode;
178 this.repoUtils = repoUtils;
179 this.configuration = config;
180 this.repositorySystem = repositorySystem;
181 this.projectBuilder = projectBuilder;
182 this.buildingRequest = buildingRequest;
183 this.licenseMappings = licenseMappings;
184
185
186 DEFAULT_DECIMAL_FORMAT.setDecimalFormatSymbols(new DecimalFormatSymbols(locale));
187
188 this.fileLengthDecimalFormat = new FileDecimalFormat(i18n, locale);
189 this.fileLengthDecimalFormat.setDecimalFormatSymbols(new DecimalFormatSymbols(locale));
190 }
191
192 @Override
193 protected String getI18Nsection() {
194 return "dependencies";
195 }
196
197
198
199
200
201 @Override
202 public void renderBody() {
203
204
205 if (!dependencies.hasDependencies()) {
206 startSection(getTitle());
207
208 paragraph(getI18nString("nolist"));
209
210 endSection();
211
212 return;
213 }
214
215
216 renderSectionProjectDependencies();
217
218
219 renderSectionProjectTransitiveDependencies();
220
221
222 renderSectionProjectDependencyGraph();
223
224
225 renderSectionDependencyLicenseListing();
226
227 if (configuration.getDependencyDetailsEnabled()) {
228
229 renderSectionDependencyFileDetails();
230 }
231 }
232
233
234
235
236
237
238
239
240 @Override
241 protected void startSection(String name) {
242 startSection(name, name);
243 }
244
245
246
247
248
249
250
251
252 protected void startSection(String anchor, String name) {
253 section = section + 1;
254
255 super.sink.anchor(HtmlTools.encodeId(anchor));
256 super.sink.anchor_();
257
258 switch (section) {
259 case 1:
260 sink.section1();
261 sink.sectionTitle1();
262 break;
263 case 2:
264 sink.section2();
265 sink.sectionTitle2();
266 break;
267 case 3:
268 sink.section3();
269 sink.sectionTitle3();
270 break;
271 case 4:
272 sink.section4();
273 sink.sectionTitle4();
274 break;
275 case 5:
276 sink.section5();
277 sink.sectionTitle5();
278 break;
279
280 default:
281
282 break;
283 }
284
285 text(name);
286
287 switch (section) {
288 case 1:
289 sink.sectionTitle1_();
290 break;
291 case 2:
292 sink.sectionTitle2_();
293 break;
294 case 3:
295 sink.sectionTitle3_();
296 break;
297 case 4:
298 sink.sectionTitle4_();
299 break;
300 case 5:
301 sink.sectionTitle5_();
302 break;
303
304 default:
305
306 break;
307 }
308 }
309
310
311
312
313 @Override
314 protected void endSection() {
315 switch (section) {
316 case 1:
317 sink.section1_();
318 break;
319 case 2:
320 sink.section2_();
321 break;
322 case 3:
323 sink.section3_();
324 break;
325 case 4:
326 sink.section4_();
327 break;
328 case 5:
329 sink.section5_();
330 break;
331
332 default:
333
334 break;
335 }
336
337 section = section - 1;
338
339 if (section < 0) {
340 throw new IllegalStateException("Too many closing sections");
341 }
342 }
343
344
345
346
347
348
349
350
351
352
353
354 private String[] getDependencyTableHeader(boolean withClassifier, boolean withOptional) {
355 String groupId = getI18nString("column.groupId");
356 String artifactId = getI18nString("column.artifactId");
357 String version = getI18nString("column.version");
358 String classifier = getI18nString("column.classifier");
359 String type = getI18nString("column.type");
360 String license = getI18nString("column.licenses");
361 String optional = getI18nString("column.optional");
362
363 if (withClassifier) {
364 if (withOptional) {
365 return new String[] {groupId, artifactId, version, classifier, type, license, optional};
366 }
367
368 return new String[] {groupId, artifactId, version, classifier, type, license};
369 }
370
371 if (withOptional) {
372 return new String[] {groupId, artifactId, version, type, license, optional};
373 }
374
375 return new String[] {groupId, artifactId, version, type, license};
376 }
377
378 private void renderSectionProjectDependencies() {
379 startSection(getTitle());
380
381
382 Map<String, List<Artifact>> dependenciesByScope = dependencies.getDependenciesByScope(false);
383
384 renderDependenciesForAllScopes(dependenciesByScope, false);
385
386 endSection();
387 }
388
389
390
391
392
393
394
395
396
397
398 private void renderDependenciesForAllScopes(Map<String, List<Artifact>> dependenciesByScope, boolean isTransitive) {
399 renderDependenciesForScope(
400 Artifact.SCOPE_COMPILE, dependenciesByScope.get(Artifact.SCOPE_COMPILE), isTransitive);
401 renderDependenciesForScope(
402 Artifact.SCOPE_RUNTIME, dependenciesByScope.get(Artifact.SCOPE_RUNTIME), isTransitive);
403 renderDependenciesForScope(Artifact.SCOPE_TEST, dependenciesByScope.get(Artifact.SCOPE_TEST), isTransitive);
404 renderDependenciesForScope(
405 Artifact.SCOPE_PROVIDED, dependenciesByScope.get(Artifact.SCOPE_PROVIDED), isTransitive);
406 renderDependenciesForScope(Artifact.SCOPE_SYSTEM, dependenciesByScope.get(Artifact.SCOPE_SYSTEM), isTransitive);
407 }
408
409 private void renderSectionProjectTransitiveDependencies() {
410 Map<String, List<Artifact>> dependenciesByScope = dependencies.getDependenciesByScope(true);
411
412 startSection(getI18nString("transitive.title"));
413
414 if (dependenciesByScope.values().isEmpty()) {
415 paragraph(getI18nString("transitive.nolist"));
416 } else {
417 paragraph(getI18nString("transitive.intro"));
418
419 renderDependenciesForAllScopes(dependenciesByScope, true);
420 }
421
422 endSection();
423 }
424
425 private void renderSectionProjectDependencyGraph() {
426 startSection(getI18nString("graph.title"));
427
428
429 renderSectionDependencyTree();
430
431 endSection();
432 }
433
434 private void renderSectionDependencyTree() {
435 StringWriter sw = new StringWriter();
436 PrintWriter pw = new PrintWriter(sw);
437
438 pw.println("");
439 pw.println("<script language=\"javascript\" type=\"text/javascript\">");
440 pw.println(" function toggleDependencyDetails( divId, imgId )");
441 pw.println(" {");
442 pw.println(" var div = document.getElementById( divId );");
443 pw.println(" var img = document.getElementById( imgId );");
444 pw.println(" if( div.style.display == '' )");
445 pw.println(" {");
446 pw.println(" div.style.display = 'none';");
447 pw.printf(" img.src='%s';%n", IMG_INFO_URL);
448 pw.printf(" img.alt='%s';%n", getI18nString("graph.icon.information"));
449 pw.println(" }");
450 pw.println(" else");
451 pw.println(" {");
452 pw.println(" div.style.display = '';");
453 pw.printf(" img.src='%s';%n", IMG_CLOSE_URL);
454 pw.printf(" img.alt='%s';%n", getI18nString("graph.icon.close"));
455 pw.println(" }");
456 pw.println(" }");
457 pw.println("</script>");
458
459 sink.rawText(sw.toString());
460
461
462 startSection(getI18nString("graph.tree.title"));
463
464 sink.list();
465 printDependencyListing(dependencyNode);
466 sink.list_();
467
468 endSection();
469 }
470
471 private void renderSectionDependencyFileDetails() {
472 startSection(getI18nString("file.details.title"));
473
474 List<Artifact> alldeps = dependencies.getAllDependencies();
475 Collections.sort(alldeps, getArtifactComparator());
476
477 resolveAtrifacts(alldeps);
478
479
480 String filename = getI18nString("file.details.column.file");
481 String size = getI18nString("file.details.column.size");
482 String entries = getI18nString("file.details.column.entries");
483 String classes = getI18nString("file.details.column.classes");
484 String packages = getI18nString("file.details.column.packages");
485 String javaVersion = getI18nString("file.details.column.javaVersion");
486 String debugInformation = getI18nString("file.details.column.debuginformation");
487 String debugInformationTitle = getI18nString("file.details.columntitle.debuginformation");
488 String debugInformationCellYes = getI18nString("file.details.cell.debuginformation.yes");
489 String debugInformationCellNo = getI18nString("file.details.cell.debuginformation.no");
490 String aSealed = getI18nString("file.details.column.sealed");
491 String sealedCellYes = getI18nString("file.details.cell.sealed.yes");
492 String sealedCellNo = getI18nString("file.details.cell.sealed.no");
493
494 int[] justification = new int[] {
495 Sink.JUSTIFY_LEFT,
496 Sink.JUSTIFY_RIGHT,
497 Sink.JUSTIFY_RIGHT,
498 Sink.JUSTIFY_RIGHT,
499 Sink.JUSTIFY_RIGHT,
500 Sink.JUSTIFY_CENTER,
501 Sink.JUSTIFY_CENTER,
502 Sink.JUSTIFY_CENTER
503 };
504
505 startTable(justification, false);
506
507 TotalCell totaldeps = new TotalCell(DEFAULT_DECIMAL_FORMAT);
508 TotalCell totaldepsize = new TotalCell(fileLengthDecimalFormat);
509 TotalCell totalentries = new TotalCell(DEFAULT_DECIMAL_FORMAT);
510 TotalCell totalclasses = new TotalCell(DEFAULT_DECIMAL_FORMAT);
511 TotalCell totalpackages = new TotalCell(DEFAULT_DECIMAL_FORMAT);
512 double highestJavaVersion = 0.0;
513 TotalCell totalDebugInformation = new TotalCell(DEFAULT_DECIMAL_FORMAT);
514 TotalCell totalsealed = new TotalCell(DEFAULT_DECIMAL_FORMAT);
515
516 boolean hasSealed = hasSealed(alldeps);
517
518
519 String[] tableHeader;
520 String[] tableHeaderTitles;
521 if (hasSealed) {
522 tableHeader =
523 new String[] {filename, size, entries, classes, packages, javaVersion, debugInformation, aSealed};
524 tableHeaderTitles = new String[] {null, null, null, null, null, null, debugInformationTitle, null};
525 } else {
526 tableHeader = new String[] {filename, size, entries, classes, packages, javaVersion, debugInformation};
527 tableHeaderTitles = new String[] {null, null, null, null, null, null, debugInformationTitle};
528 }
529 tableHeader(tableHeader, tableHeaderTitles);
530
531
532 for (Artifact artifact : alldeps) {
533 if (artifact.getFile() == null) {
534 log.warn("Artifact " + artifact.getId() + " has no file"
535 + " and won't be listed in dependency files details.");
536 continue;
537 }
538
539 File artifactFile = dependencies.getFile(artifact);
540
541 totaldeps.incrementTotal(artifact.getScope());
542 totaldepsize.addTotal(artifactFile.length(), artifact.getScope());
543
544 if (JAR_SUBTYPE.contains(artifact.getType().toLowerCase())) {
545 try {
546 JarData jarDetails = dependencies.getJarDependencyDetails(artifact);
547
548 String debugInformationCellValue = debugInformationCellNo;
549 if (jarDetails.isDebugPresent()) {
550 debugInformationCellValue = debugInformationCellYes;
551 totalDebugInformation.incrementTotal(artifact.getScope());
552 }
553
554 totalentries.addTotal(jarDetails.getNumEntries(), artifact.getScope());
555 totalclasses.addTotal(jarDetails.getNumClasses(), artifact.getScope());
556 totalpackages.addTotal(jarDetails.getNumPackages(), artifact.getScope());
557
558 try {
559 if (jarDetails.getJdkRevision() != null) {
560 highestJavaVersion =
561 Math.max(highestJavaVersion, Double.parseDouble(jarDetails.getJdkRevision()));
562 }
563 } catch (NumberFormatException e) {
564
565 }
566
567 String sealedCellValue = sealedCellNo;
568 if (jarDetails.isSealed()) {
569 sealedCellValue = sealedCellYes;
570 totalsealed.incrementTotal(artifact.getScope());
571 }
572
573 String name = artifactFile.getName();
574 String fileLength = fileLengthDecimalFormat.format(artifactFile.length());
575
576 if (artifactFile.isDirectory()) {
577 File parent = artifactFile.getParentFile();
578 name = parent.getParentFile().getName() + '/' + parent.getName() + '/' + artifactFile.getName();
579 fileLength = "-";
580 }
581
582 tableRow(hasSealed, new String[] {
583 name,
584 fileLength,
585 DEFAULT_DECIMAL_FORMAT.format(jarDetails.getNumEntries()),
586 DEFAULT_DECIMAL_FORMAT.format(jarDetails.getNumClasses()),
587 DEFAULT_DECIMAL_FORMAT.format(jarDetails.getNumPackages()),
588 jarDetails.getJdkRevision(),
589 debugInformationCellValue,
590 sealedCellValue
591 });
592 } catch (IOException e) {
593 createExceptionInfoTableRow(artifact, artifactFile, e, hasSealed);
594 }
595 } else {
596 tableRow(hasSealed, new String[] {
597 artifactFile.getName(),
598 fileLengthDecimalFormat.format(artifactFile.length()),
599 "",
600 "",
601 "",
602 "",
603 "",
604 ""
605 });
606 }
607 }
608
609
610 tableHeader[0] = getI18nString("file.details.total");
611 tableHeader(tableHeader);
612
613 justification[0] = Sink.JUSTIFY_RIGHT;
614 justification[6] = Sink.JUSTIFY_RIGHT;
615
616 for (int i = -1; i < TotalCell.SCOPES_COUNT; i++) {
617 if (totaldeps.getTotal(i) > 0) {
618 tableRow(hasSealed, new String[] {
619 totaldeps.getTotalString(i),
620 totaldepsize.getTotalString(i),
621 totalentries.getTotalString(i),
622 totalclasses.getTotalString(i),
623 totalpackages.getTotalString(i),
624 (i < 0) ? String.valueOf(highestJavaVersion) : "",
625 totalDebugInformation.getTotalString(i),
626 totalsealed.getTotalString(i)
627 });
628 }
629 }
630
631 endTable();
632 endSection();
633 }
634
635
636 private void tableHeader(String[] content, String[] titles) {
637 sink.tableRow();
638
639 if (content != null) {
640 if (titles != null && content.length != titles.length) {
641 throw new IllegalArgumentException("Length of title array must equal the length of the content array");
642 }
643
644 for (int i = 0; i < content.length; i++) {
645 if (titles != null) {
646 tableHeaderCell(content[i], titles[i]);
647 } else {
648 tableHeaderCell(content[i]);
649 }
650 }
651 }
652
653 sink.tableRow_();
654 }
655
656 private void tableHeaderCell(String text, String title) {
657 if (title != null) {
658 sink.tableHeaderCell(new SinkEventAttributeSet(SinkEventAttributes.TITLE, title));
659 } else {
660 sink.tableHeaderCell();
661 }
662
663 text(text);
664
665 sink.tableHeaderCell_();
666 }
667
668 private void tableRow(boolean fullRow, String[] content) {
669 sink.tableRow();
670
671 int count = fullRow ? content.length : (content.length - 1);
672
673 for (int i = 0; i < count; i++) {
674 tableCell(content[i]);
675 }
676
677 sink.tableRow_();
678 }
679
680 private void createExceptionInfoTableRow(Artifact artifact, File artifactFile, Exception e, boolean hasSealed) {
681 tableRow(
682 hasSealed,
683 new String[] {artifact.getId(), artifactFile.getAbsolutePath(), e.getMessage(), "", "", "", "", ""});
684 }
685
686 private void renderSectionDependencyLicenseListing() {
687 startSection(getI18nString("graph.tables.licenses"));
688 printGroupedLicenses();
689 endSection();
690 }
691
692 private void renderDependenciesForScope(String scope, List<Artifact> artifacts, boolean isTransitive) {
693 if (artifacts != null) {
694 boolean withClassifier = hasClassifier(artifacts);
695 boolean withOptional = hasOptional(artifacts);
696 String[] tableHeader = getDependencyTableHeader(withClassifier, withOptional);
697
698
699 Collections.sort(artifacts, getArtifactComparator());
700
701 String anchorByScope = (isTransitive
702 ? getI18nString("transitive.title") + "_" + scope
703 : getI18nString("title") + "_" + scope);
704 startSection(anchorByScope, scope);
705
706 paragraph(getI18nString("intro." + scope));
707
708 startTable();
709 tableHeader(tableHeader);
710 for (Artifact artifact : artifacts) {
711 renderArtifactRow(artifact, withClassifier, withOptional);
712 }
713 endTable();
714
715 endSection();
716 }
717 }
718
719 private Comparator<Artifact> getArtifactComparator() {
720 return new Comparator<Artifact>() {
721 public int compare(Artifact a1, Artifact a2) {
722
723 if (a1.isOptional() && !a2.isOptional()) {
724 return +1;
725 } else if (!a1.isOptional() && a2.isOptional()) {
726 return -1;
727 } else {
728 return a1.compareTo(a2);
729 }
730 }
731 };
732 }
733
734
735
736
737
738
739
740 private void renderArtifactRow(Artifact artifact, boolean withClassifier, boolean withOptional) {
741 String isOptional =
742 artifact.isOptional() ? getI18nString("column.isOptional") : getI18nString("column.isNotOptional");
743
744 String url = ProjectInfoReportUtils.getArtifactUrl(repositorySystem, artifact, projectBuilder, buildingRequest);
745 String artifactIdCell = ProjectInfoReportUtils.getArtifactIdCell(artifact.getArtifactId(), url);
746
747 MavenProject artifactProject;
748 StringBuilder sb = new StringBuilder();
749 try {
750 artifactProject = repoUtils.getMavenProjectFromRepository(artifact);
751
752 List<License> licenses = artifactProject.getLicenses();
753 for (License license : licenses) {
754 String name = license.getName();
755 if (licenseMappings != null && licenseMappings.containsKey(name)) {
756 name = licenseMappings.get(name);
757 }
758 sb.append(ProjectInfoReportUtils.getArtifactIdCell(name, license.getUrl()));
759 }
760 } catch (ProjectBuildingException e) {
761 if (log.isDebugEnabled()) {
762 log.debug("Unable to create Maven project from repository for artifact '" + artifact.getId() + "'", e);
763 } else {
764 log.info("Unable to create Maven project from repository for artifact '" + artifact.getId()
765 + "', for more information run with -X");
766 }
767 }
768
769 String[] content;
770 if (withClassifier) {
771 content = new String[] {
772 artifact.getGroupId(),
773 artifactIdCell,
774 artifact.getVersion(),
775 artifact.getClassifier(),
776 artifact.getType(),
777 sb.toString(),
778 isOptional
779 };
780 } else {
781 content = new String[] {
782 artifact.getGroupId(),
783 artifactIdCell,
784 artifact.getVersion(),
785 artifact.getType(),
786 sb.toString(),
787 isOptional
788 };
789 }
790
791 tableRow(withOptional, content);
792 }
793
794 private void printDependencyListing(DependencyNode node) {
795 Artifact artifact = node.getArtifact();
796 String id = artifact.getId();
797 String dependencyDetailId = "_dep" + idCounter++;
798 String imgId = "_img" + idCounter++;
799
800 sink.listItem();
801
802 sink.text(id + (StringUtils.isNotEmpty(artifact.getScope()) ? " (" + artifact.getScope() + ") " : " "));
803
804 String javascript = String.format(
805 "<img id=\"%s\" src=\"%s\" alt=\"%s\""
806 + " onclick=\"toggleDependencyDetails( '%s', '%s' );\""
807 + " style=\"cursor: pointer; vertical-align: text-bottom;\" />",
808 imgId, IMG_INFO_URL, getI18nString("graph.icon.information"), dependencyDetailId, imgId);
809
810 sink.rawText(javascript);
811
812 printDescriptionsAndURLs(node, dependencyDetailId);
813
814 if (!node.getChildren().isEmpty()) {
815 boolean toBeIncluded = false;
816 List<DependencyNode> subList = new ArrayList<DependencyNode>();
817 for (DependencyNode dep : node.getChildren()) {
818 if (dependencies.getAllDependencies().contains(dep.getArtifact())) {
819 subList.add(dep);
820 toBeIncluded = true;
821 }
822 }
823
824 if (toBeIncluded) {
825 sink.list();
826 for (DependencyNode dep : subList) {
827 printDependencyListing(dep);
828 }
829 sink.list_();
830 }
831 }
832
833 sink.listItem_();
834 }
835
836 private void printDescriptionsAndURLs(DependencyNode node, String uid) {
837 Artifact artifact = node.getArtifact();
838 String id = artifact.getId();
839 String unknownLicenseMessage = getI18nString("graph.tables.unknown");
840
841 sink.rawText("<div id=\"" + uid + "\" style=\"display:none\">");
842
843 if (!Artifact.SCOPE_SYSTEM.equals(artifact.getScope())) {
844 try {
845 MavenProject artifactProject = repoUtils.getMavenProjectFromRepository(artifact);
846 String artifactDescription = artifactProject.getDescription();
847 String artifactUrl = artifactProject.getUrl();
848 String artifactName = artifactProject.getName();
849
850 List<License> licenses = artifactProject.getLicenses();
851
852 sink.table();
853 sink.tableRows(null, false);
854
855 sink.tableRow();
856 sink.tableHeaderCell();
857 sink.text(artifactName);
858 sink.tableHeaderCell_();
859 sink.tableRow_();
860
861 sink.tableRow();
862 sink.tableCell();
863
864 sink.paragraph();
865 sink.bold();
866 sink.text(getI18nString("column.description") + ": ");
867 sink.bold_();
868 if (StringUtils.isNotEmpty(artifactDescription)) {
869 sink.text(artifactDescription);
870 } else {
871 sink.text(getI18nString("index", "nodescription"));
872 }
873 sink.paragraph_();
874
875 if (StringUtils.isNotEmpty(artifactUrl)) {
876 sink.paragraph();
877 sink.bold();
878 sink.text(getI18nString("column.url") + ": ");
879 sink.bold_();
880 if (ProjectInfoReportUtils.isArtifactUrlValid(artifactUrl)) {
881 sink.link(artifactUrl);
882 sink.text(artifactUrl);
883 sink.link_();
884 } else {
885 sink.text(artifactUrl);
886 }
887 sink.paragraph_();
888 }
889
890 sink.paragraph();
891 sink.bold();
892 sink.text(getI18nString("licenses", "title") + ": ");
893 sink.bold_();
894 if (!licenses.isEmpty()) {
895
896 for (Iterator<License> it = licenses.iterator(); it.hasNext(); ) {
897 License license = it.next();
898
899 String licenseName = license.getName();
900 if (licenseMappings != null && licenseMappings.containsKey(licenseName)) {
901 licenseName = licenseMappings.get(licenseName);
902 }
903 if (StringUtils.isEmpty(licenseName)) {
904 licenseName = getI18nString("unnamed");
905 }
906
907 String licenseUrl = license.getUrl();
908
909 if (licenseUrl != null) {
910 sink.link(licenseUrl);
911 }
912 sink.text(licenseName);
913
914 if (licenseUrl != null) {
915 sink.link_();
916 }
917
918 if (it.hasNext()) {
919 sink.text(", ");
920 }
921
922 licenseMap.put(licenseName, artifactName);
923 }
924 } else {
925 sink.text(getI18nString("licenses", "nolicense"));
926
927 licenseMap.put(unknownLicenseMessage, artifactName);
928 }
929 sink.paragraph_();
930
931 sink.tableCell_();
932 sink.tableRow_();
933
934 sink.tableRows_();
935 sink.table_();
936 } catch (ProjectBuildingException e) {
937 sink.text(getI18nString("index", "nodescription"));
938 if (log.isDebugEnabled()) {
939 log.debug(
940 "Unable to create Maven project from repository for artifact '" + artifact.getId() + "'",
941 e);
942 } else {
943 log.info("Unable to create Maven project from repository for artifact '" + artifact.getId()
944 + "', for more information run with -X");
945 }
946 }
947 } else {
948 sink.table();
949 sink.tableRows(null, false);
950
951 sink.tableRow();
952 sink.tableHeaderCell();
953 sink.text(id);
954 sink.tableHeaderCell_();
955 sink.tableRow_();
956
957 sink.tableRow();
958 sink.tableCell();
959
960 sink.paragraph();
961 sink.bold();
962 sink.text(getI18nString("column.description") + ": ");
963 sink.bold_();
964 sink.text(getI18nString("index", "nodescription"));
965 sink.paragraph_();
966
967 if (artifact.getFile() != null) {
968 sink.paragraph();
969 sink.bold();
970 sink.text(getI18nString("column.url") + ": ");
971 sink.bold_();
972 sink.text(artifact.getFile().getAbsolutePath());
973 sink.paragraph_();
974 }
975
976 sink.tableCell_();
977 sink.tableRow_();
978
979 sink.tableRows_();
980 sink.table_();
981 }
982
983 sink.rawText("</div>");
984 }
985
986 private void printGroupedLicenses() {
987 for (Map.Entry<String, Object> entry : licenseMap.entrySet()) {
988 String licenseName = entry.getKey();
989 if (StringUtils.isEmpty(licenseName)) {
990 licenseName = getI18nString("unnamed");
991 }
992
993 sink.paragraph();
994 sink.bold();
995 sink.text(licenseName);
996 sink.text(": ");
997 sink.bold_();
998
999 @SuppressWarnings("unchecked")
1000 SortedSet<String> projects = (SortedSet<String>) entry.getValue();
1001
1002 for (Iterator<String> iterator = projects.iterator(); iterator.hasNext(); ) {
1003 String projectName = iterator.next();
1004 sink.text(projectName);
1005 if (iterator.hasNext()) {
1006 sink.text(", ");
1007 }
1008 }
1009
1010 sink.paragraph_();
1011 }
1012 }
1013
1014
1015
1016
1017
1018
1019 private void resolveAtrifacts(List<Artifact> artifacts) {
1020 for (Artifact artifact : artifacts) {
1021
1022 if (artifact.getFile() == null) {
1023 if (Artifact.SCOPE_SYSTEM.equals(artifact.getScope())) {
1024
1025 continue;
1026 }
1027
1028 try {
1029 repoUtils.resolve(artifact);
1030 } catch (ArtifactResolverException e) {
1031 log.error("Artifact " + artifact.getId() + " can't be resolved.", e);
1032 continue;
1033 }
1034
1035 if (artifact.getFile() == null) {
1036 log.error("Artifact " + artifact.getId() + " has no file, even after resolution.");
1037 }
1038 }
1039 }
1040 }
1041
1042
1043
1044
1045
1046 private boolean hasClassifier(List<Artifact> artifacts) {
1047 for (Artifact artifact : artifacts) {
1048 if (StringUtils.isNotEmpty(artifact.getClassifier())) {
1049 return true;
1050 }
1051 }
1052
1053 return false;
1054 }
1055
1056
1057
1058
1059
1060 private boolean hasOptional(List<Artifact> artifacts) {
1061 for (Artifact artifact : artifacts) {
1062 if (artifact.isOptional()) {
1063 return true;
1064 }
1065 }
1066
1067 return false;
1068 }
1069
1070
1071
1072
1073
1074 private boolean hasSealed(List<Artifact> artifacts) {
1075 for (Artifact artifact : artifacts) {
1076 if (artifact.getFile() != null
1077 && JAR_SUBTYPE.contains(artifact.getType().toLowerCase())) {
1078 try {
1079 JarData jarDetails = dependencies.getJarDependencyDetails(artifact);
1080 if (jarDetails.isSealed()) {
1081 return true;
1082 }
1083 } catch (IOException e) {
1084 log.error("Artifact " + artifact.getId() + " caused IOException: " + e.getMessage(), e);
1085 }
1086 }
1087 }
1088 return false;
1089 }
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102 static class FileDecimalFormat extends DecimalFormat {
1103 private static final long serialVersionUID = 4062503546523610081L;
1104
1105 private final I18N i18n;
1106
1107 private final Locale locale;
1108
1109
1110
1111
1112
1113
1114
1115 FileDecimalFormat(I18N i18n, Locale locale) {
1116 super("###0.#");
1117
1118 this.i18n = i18n;
1119 this.locale = locale;
1120 }
1121
1122
1123 @Override
1124 public StringBuffer format(long fs, StringBuffer result, FieldPosition fieldPosition) {
1125 if (fs > 1000 * 1000 * 1000) {
1126 result = super.format((float) fs / (1000 * 1000 * 1000), result, fieldPosition);
1127 result.append(" ").append(getString("report.dependencies.file.details.column.size.gb"));
1128 return result;
1129 }
1130
1131 if (fs > 1000 * 1000) {
1132 result = super.format((float) fs / (1000 * 1000), result, fieldPosition);
1133 result.append(" ").append(getString("report.dependencies.file.details.column.size.mb"));
1134 return result;
1135 }
1136
1137 result = super.format((float) fs / (1000), result, fieldPosition);
1138 result.append(" ").append(getString("report.dependencies.file.details.column.size.kb"));
1139 return result;
1140 }
1141
1142 private String getString(String key) {
1143 return i18n.getString("project-info-reports", locale, key);
1144 }
1145 }
1146
1147
1148
1149
1150 static class TotalCell {
1151 static final int SCOPES_COUNT = 5;
1152
1153 final DecimalFormat decimalFormat;
1154
1155 long total = 0;
1156
1157 long totalCompileScope = 0;
1158
1159 long totalTestScope = 0;
1160
1161 long totalRuntimeScope = 0;
1162
1163 long totalProvidedScope = 0;
1164
1165 long totalSystemScope = 0;
1166
1167 TotalCell(DecimalFormat decimalFormat) {
1168 this.decimalFormat = decimalFormat;
1169 }
1170
1171 void incrementTotal(String scope) {
1172 addTotal(1, scope);
1173 }
1174
1175 static String getScope(int index) {
1176 switch (index) {
1177 case 0:
1178 return Artifact.SCOPE_COMPILE;
1179 case 1:
1180 return Artifact.SCOPE_TEST;
1181 case 2:
1182 return Artifact.SCOPE_RUNTIME;
1183 case 3:
1184 return Artifact.SCOPE_PROVIDED;
1185 case 4:
1186 return Artifact.SCOPE_SYSTEM;
1187 default:
1188 return null;
1189 }
1190 }
1191
1192 long getTotal(int index) {
1193 switch (index) {
1194 case 0:
1195 return totalCompileScope;
1196 case 1:
1197 return totalTestScope;
1198 case 2:
1199 return totalRuntimeScope;
1200 case 3:
1201 return totalProvidedScope;
1202 case 4:
1203 return totalSystemScope;
1204 default:
1205 return total;
1206 }
1207 }
1208
1209 String getTotalString(int index) {
1210 long totalString = getTotal(index);
1211
1212 if (totalString <= 0) {
1213 return "";
1214 }
1215
1216 StringBuilder sb = new StringBuilder();
1217 if (index >= 0) {
1218 sb.append(getScope(index)).append(": ");
1219 }
1220 sb.append(decimalFormat.format(getTotal(index)));
1221 return sb.toString();
1222 }
1223
1224 void addTotal(long add, String scope) {
1225 total += add;
1226
1227 if (Artifact.SCOPE_COMPILE.equals(scope)) {
1228 totalCompileScope += add;
1229 } else if (Artifact.SCOPE_TEST.equals(scope)) {
1230 totalTestScope += add;
1231 } else if (Artifact.SCOPE_RUNTIME.equals(scope)) {
1232 totalRuntimeScope += add;
1233 } else if (Artifact.SCOPE_PROVIDED.equals(scope)) {
1234 totalProvidedScope += add;
1235 } else if (Artifact.SCOPE_SYSTEM.equals(scope)) {
1236 totalSystemScope += add;
1237 }
1238 }
1239
1240
1241 public String toString() {
1242 StringBuilder sb = new StringBuilder();
1243 sb.append(decimalFormat.format(total));
1244 sb.append(" (");
1245
1246 boolean needSeparator = false;
1247 for (int i = 0; i < SCOPES_COUNT; i++) {
1248 if (getTotal(i) > 0) {
1249 if (needSeparator) {
1250 sb.append(", ");
1251 }
1252 sb.append(getTotalString(i));
1253 needSeparator = true;
1254 }
1255 }
1256
1257 sb.append(")");
1258
1259 return sb.toString();
1260 }
1261 }
1262 }