1 package org.apache.maven.report.projectinfo.dependencies.renderer;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.File;
23 import java.io.IOException;
24 import java.net.URL;
25 import java.security.NoSuchAlgorithmException;
26 import java.security.SecureRandom;
27 import java.text.DecimalFormat;
28 import java.text.DecimalFormatSymbols;
29 import java.text.FieldPosition;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.Comparator;
33 import java.util.HashMap;
34 import java.util.HashSet;
35 import java.util.Iterator;
36 import java.util.List;
37 import java.util.Locale;
38 import java.util.Map;
39 import java.util.Set;
40 import java.util.SortedSet;
41 import java.util.TreeSet;
42
43 import org.apache.maven.artifact.Artifact;
44 import org.apache.maven.artifact.factory.ArtifactFactory;
45 import org.apache.maven.artifact.repository.ArtifactRepository;
46 import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
47 import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
48 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
49 import org.apache.maven.doxia.parser.Parser;
50 import org.apache.maven.doxia.sink.Sink;
51 import org.apache.maven.model.License;
52 import org.apache.maven.plugin.logging.Log;
53 import org.apache.maven.project.MavenProject;
54 import org.apache.maven.project.MavenProjectBuilder;
55 import org.apache.maven.project.ProjectBuildingException;
56 import org.apache.maven.report.projectinfo.ProjectInfoReportUtils;
57 import org.apache.maven.report.projectinfo.dependencies.Dependencies;
58 import org.apache.maven.report.projectinfo.dependencies.DependenciesReportConfiguration;
59 import org.apache.maven.report.projectinfo.dependencies.RepositoryUtils;
60 import org.apache.maven.reporting.AbstractMavenReportRenderer;
61 import org.apache.maven.settings.Settings;
62 import org.apache.maven.shared.dependency.tree.DependencyNode;
63 import org.apache.maven.shared.jar.JarData;
64 import org.codehaus.plexus.i18n.I18N;
65 import org.codehaus.plexus.util.StringUtils;
66
67
68
69
70
71
72
73 public class DependenciesRenderer
74 extends AbstractMavenReportRenderer
75 {
76
77 private static final String IMG_INFO_URL = "./images/icon_info_sml.gif";
78
79
80 private static final String IMG_CLOSE_URL = "./images/close.gif";
81
82
83 private static final SecureRandom RANDOM;
84
85
86 protected static final DecimalFormat DEFAULT_DECIMAL_FORMAT = new DecimalFormat( "#,##0" );
87
88 private static final Set JAR_SUBTYPE = new HashSet();
89
90
91
92
93 private static final String JAVASCRIPT;
94
95 private final DependencyNode dependencyTreeNode;
96
97 private final Dependencies dependencies;
98
99 private final DependenciesReportConfiguration configuration;
100
101 private final Locale locale;
102
103 private final I18N i18n;
104
105 private final Log log;
106
107 private final Settings settings;
108
109 private final RepositoryUtils repoUtils;
110
111
112 private final DecimalFormat fileLengthDecimalFormat;
113
114
115
116
117 private Map licenseMap = new HashMap()
118 {
119
120 public Object put( Object key, Object value )
121 {
122
123 SortedSet valueList = (SortedSet) get( key );
124 if ( valueList == null )
125 {
126 valueList = new TreeSet();
127 }
128 valueList.add( value );
129 return super.put( key, valueList );
130 }
131 };
132
133 private final ArtifactFactory artifactFactory;
134
135 private final MavenProjectBuilder mavenProjectBuilder;
136
137 private final List remoteRepositories;
138
139 private final ArtifactRepository localRepository;
140
141 static
142 {
143 JAR_SUBTYPE.add( "jar" );
144 JAR_SUBTYPE.add( "war" );
145 JAR_SUBTYPE.add( "ear" );
146 JAR_SUBTYPE.add( "sar" );
147 JAR_SUBTYPE.add( "rar" );
148 JAR_SUBTYPE.add( "par" );
149 JAR_SUBTYPE.add( "ejb" );
150
151 try
152 {
153 RANDOM = SecureRandom.getInstance( "SHA1PRNG" );
154 }
155 catch ( NoSuchAlgorithmException e )
156 {
157 throw new RuntimeException( e );
158 }
159
160 StringBuffer sb = new StringBuffer();
161 sb.append( "<script language=\"javascript\" type=\"text/javascript\">" ).append( "\n" );
162 sb.append( " function toggleDependencyDetail( divId, imgId )" ).append( "\n" );
163 sb.append( " {" ).append( "\n" );
164 sb.append( " var div = document.getElementById( divId );" ).append( "\n" );
165 sb.append( " var img = document.getElementById( imgId );" ).append( "\n" );
166 sb.append( " if( div.style.display == '' )" ).append( "\n" );
167 sb.append( " {" ).append( "\n" );
168 sb.append( " div.style.display = 'none';" ).append( "\n" );
169 sb.append( " img.src='" + IMG_INFO_URL + "';" ).append( "\n" );
170 sb.append( " }" ).append( "\n" );
171 sb.append( " else" ).append( "\n" );
172 sb.append( " {" ).append( "\n" );
173 sb.append( " div.style.display = '';" ).append( "\n" );
174 sb.append( " img.src='" + IMG_CLOSE_URL + "';" ).append( "\n" );
175 sb.append( " }" ).append( "\n" );
176 sb.append( " }" ).append( "\n" );
177 sb.append( "</script>" ).append( "\n" );
178 JAVASCRIPT = sb.toString();
179 }
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198 public DependenciesRenderer( Sink sink, Locale locale, I18N i18n, Log log, Settings settings,
199 Dependencies dependencies, DependencyNode dependencyTreeNode,
200 DependenciesReportConfiguration config, RepositoryUtils repoUtils,
201 ArtifactFactory artifactFactory, MavenProjectBuilder mavenProjectBuilder,
202 List remoteRepositories, ArtifactRepository localRepository )
203 {
204 super( sink );
205
206 this.locale = locale;
207 this.i18n = i18n;
208 this.log = log;
209 this.settings = settings;
210 this.dependencies = dependencies;
211 this.dependencyTreeNode = dependencyTreeNode;
212 this.repoUtils = repoUtils;
213 this.configuration = config;
214 this.artifactFactory = artifactFactory;
215 this.mavenProjectBuilder = mavenProjectBuilder;
216 this.remoteRepositories = remoteRepositories;
217 this.localRepository = localRepository;
218
219
220 DEFAULT_DECIMAL_FORMAT.setDecimalFormatSymbols( new DecimalFormatSymbols( locale ) );
221
222 this.fileLengthDecimalFormat = new FileDecimalFormat( i18n, locale );
223 this.fileLengthDecimalFormat.setDecimalFormatSymbols( new DecimalFormatSymbols( locale ) );
224 }
225
226
227
228
229
230
231 public String getTitle()
232 {
233 return getReportString( "report.dependencies.title" );
234 }
235
236
237 public void renderBody()
238 {
239
240
241 if ( !dependencies.hasDependencies() )
242 {
243 startSection( getTitle() );
244
245
246 paragraph( getReportString( "report.dependencies.nolist" ) );
247
248 endSection();
249
250 return;
251 }
252
253
254 renderSectionProjectDependencies();
255
256
257 renderSectionProjectTransitiveDependencies();
258
259
260 renderSectionProjectDependencyGraph();
261
262
263 renderSectionDependencyLicenseListing();
264
265 if ( configuration.getDependencyDetailsEnabled() )
266 {
267
268 renderSectionDependencyFileDetails();
269 }
270
271 if ( configuration.getDependencyLocationsEnabled() )
272 {
273
274 renderSectionDependencyRepositoryLocations();
275 }
276 }
277
278
279
280
281
282
283
284
285
286
287
288 private String[] getDependencyTableHeader( boolean withClassifier, boolean withOptional )
289 {
290 String groupId = getReportString( "report.dependencies.column.groupId" );
291 String artifactId = getReportString( "report.dependencies.column.artifactId" );
292 String version = getReportString( "report.dependencies.column.version" );
293 String classifier = getReportString( "report.dependencies.column.classifier" );
294 String type = getReportString( "report.dependencies.column.type" );
295 String optional = getReportString( "report.dependencies.column.optional" );
296
297 if ( withClassifier )
298 {
299 if ( withOptional )
300 {
301 return new String[] { groupId, artifactId, version, classifier, type, optional };
302 }
303
304 return new String[] { groupId, artifactId, version, classifier, type };
305 }
306
307 if ( withOptional )
308 {
309 return new String[] { groupId, artifactId, version, type, optional };
310 }
311
312 return new String[] { groupId, artifactId, version, type };
313 }
314
315 private void renderSectionProjectDependencies()
316 {
317 startSection( getTitle() );
318
319
320 Map dependenciesByScope = dependencies.getDependenciesByScope( false );
321
322 renderDependenciesForAllScopes( dependenciesByScope );
323
324 endSection();
325 }
326
327
328
329
330
331
332
333
334
335 private void renderDependenciesForAllScopes( Map dependenciesByScope )
336 {
337 renderDependenciesForScope( Artifact.SCOPE_COMPILE, (List) dependenciesByScope.get( Artifact.SCOPE_COMPILE ) );
338 renderDependenciesForScope( Artifact.SCOPE_RUNTIME, (List) dependenciesByScope.get( Artifact.SCOPE_RUNTIME ) );
339 renderDependenciesForScope( Artifact.SCOPE_TEST, (List) dependenciesByScope.get( Artifact.SCOPE_TEST ) );
340 renderDependenciesForScope( Artifact.SCOPE_PROVIDED,
341 (List) dependenciesByScope.get( Artifact.SCOPE_PROVIDED ) );
342 renderDependenciesForScope( Artifact.SCOPE_SYSTEM, (List) dependenciesByScope.get( Artifact.SCOPE_SYSTEM ) );
343 }
344
345 private void renderSectionProjectTransitiveDependencies()
346 {
347 Map dependenciesByScope = dependencies.getDependenciesByScope( true );
348
349 startSection( getReportString( "report.transitivedependencies.title" ) );
350
351 if ( dependenciesByScope.values().isEmpty() )
352 {
353 paragraph( getReportString( "report.transitivedependencies.nolist" ) );
354 }
355 else
356 {
357 paragraph( getReportString( "report.transitivedependencies.intro" ) );
358
359 renderDependenciesForAllScopes( dependenciesByScope );
360 }
361
362 endSection();
363 }
364
365 private void renderSectionProjectDependencyGraph()
366 {
367 startSection( getReportString( "report.dependencies.graph.title" ) );
368
369
370 renderSectionDependencyTree();
371
372 endSection();
373 }
374
375 private void renderSectionDependencyTree()
376 {
377 sink.rawText( JAVASCRIPT );
378
379
380 startSection( getReportString( "report.dependencies.graph.tree.title" ) );
381
382 sink.list();
383 printDependencyListing( dependencyTreeNode );
384 sink.list_();
385
386 endSection();
387 }
388
389 private void renderSectionDependencyFileDetails()
390 {
391 startSection( getReportString( "report.dependencies.file.details.title" ) );
392
393 List alldeps = dependencies.getAllDependencies();
394 Collections.sort( alldeps, getArtifactComparator() );
395
396
397 String filename = getReportString( "report.dependencies.file.details.column.file" );
398 String size = getReportString( "report.dependencies.file.details.column.size" );
399 String entries = getReportString( "report.dependencies.file.details.column.entries" );
400 String classes = getReportString( "report.dependencies.file.details.column.classes" );
401 String packages = getReportString( "report.dependencies.file.details.column.packages" );
402 String jdkrev = getReportString( "report.dependencies.file.details.column.jdkrev" );
403 String debug = getReportString( "report.dependencies.file.details.column.debug" );
404 String sealed = getReportString( "report.dependencies.file.details.column.sealed" );
405
406 int[] justification =
407 new int[] { Parser.JUSTIFY_LEFT, Parser.JUSTIFY_RIGHT, Parser.JUSTIFY_RIGHT, Parser.JUSTIFY_RIGHT,
408 Parser.JUSTIFY_RIGHT, Parser.JUSTIFY_CENTER, Parser.JUSTIFY_CENTER, Parser.JUSTIFY_CENTER };
409
410 startTable();
411
412 sink.tableRows( justification, true );
413
414 TotalCell totaldeps = new TotalCell( DEFAULT_DECIMAL_FORMAT );
415 TotalCell totaldepsize = new TotalCell( fileLengthDecimalFormat );
416 TotalCell totalentries = new TotalCell( DEFAULT_DECIMAL_FORMAT );
417 TotalCell totalclasses = new TotalCell( DEFAULT_DECIMAL_FORMAT );
418 TotalCell totalpackages = new TotalCell( DEFAULT_DECIMAL_FORMAT );
419 double highestjdk = 0.0;
420 TotalCell totaldebug = new TotalCell( DEFAULT_DECIMAL_FORMAT );
421 TotalCell totalsealed = new TotalCell( DEFAULT_DECIMAL_FORMAT );
422
423 boolean hasSealed = hasSealed( alldeps );
424
425
426 String[] tableHeader;
427 if ( hasSealed )
428 {
429 tableHeader = new String[] { filename, size, entries, classes, packages, jdkrev, debug, sealed };
430 }
431 else
432 {
433 tableHeader = new String[] { filename, size, entries, classes, packages, jdkrev, debug };
434 }
435 tableHeader( tableHeader );
436
437
438 for ( Iterator it = alldeps.iterator(); it.hasNext(); )
439 {
440 Artifact artifact = (Artifact) it.next();
441
442 if ( artifact.getFile() == null )
443 {
444 log.error( "Artifact: " + artifact.getId() + " has no file." );
445 continue;
446 }
447
448 File artifactFile = artifact.getFile();
449
450 totaldeps.incrementTotal( artifact.getScope() );
451 totaldepsize.addTotal( artifactFile.length(), artifact.getScope() );
452
453 if ( JAR_SUBTYPE.contains( artifact.getType().toLowerCase() ) )
454 {
455 try
456 {
457 JarData jarDetails = dependencies.getJarDependencyDetails( artifact );
458
459 String debugstr = "release";
460 if ( jarDetails.isDebugPresent() )
461 {
462 debugstr = "debug";
463 totaldebug.incrementTotal( artifact.getScope() );
464 }
465
466 totalentries.addTotal( jarDetails.getNumEntries(), artifact.getScope() );
467 totalclasses.addTotal( jarDetails.getNumClasses(), artifact.getScope() );
468 totalpackages.addTotal( jarDetails.getNumPackages(), artifact.getScope() );
469
470 try
471 {
472 if ( jarDetails.getJdkRevision() != null )
473 {
474 highestjdk = Math.max( highestjdk, Double.parseDouble( jarDetails.getJdkRevision() ) );
475 }
476 }
477 catch ( NumberFormatException e )
478 {
479
480 }
481
482 String sealedstr = "";
483 if ( jarDetails.isSealed() )
484 {
485 sealedstr = "sealed";
486 totalsealed.incrementTotal( artifact.getScope() );
487 }
488
489 tableRow( hasSealed, new String[] { artifactFile.getName(),
490 fileLengthDecimalFormat.format( artifactFile.length() ),
491 DEFAULT_DECIMAL_FORMAT.format( jarDetails.getNumEntries() ),
492 DEFAULT_DECIMAL_FORMAT.format( jarDetails.getNumClasses() ),
493 DEFAULT_DECIMAL_FORMAT.format( jarDetails.getNumPackages() ), jarDetails.getJdkRevision(),
494 debugstr, sealedstr } );
495 }
496 catch ( IOException e )
497 {
498 createExceptionInfoTableRow( artifact, artifactFile, e, hasSealed );
499 }
500 }
501 else
502 {
503 tableRow( hasSealed, new String[] { artifactFile.getName(),
504 fileLengthDecimalFormat.format( artifactFile.length() ), "", "", "", "", "", "" } );
505 }
506 }
507
508
509 tableHeader[0] = getReportString( "report.dependencies.file.details.total" );
510 tableHeader( tableHeader );
511
512 justification[0] = Parser.JUSTIFY_RIGHT;
513 justification[6] = Parser.JUSTIFY_RIGHT;
514
515 for ( int i = -1; i < TotalCell.SCOPES_COUNT; i++ )
516 {
517 if ( totaldeps.getTotal( i ) > 0 )
518 {
519 tableRow( hasSealed, new String[] { totaldeps.getTotalString( i ), totaldepsize.getTotalString( i ),
520 totalentries.getTotalString( i ), totalclasses.getTotalString( i ),
521 totalpackages.getTotalString( i ), ( i < 0 ) ? String.valueOf( highestjdk ) : "",
522 totaldebug.getTotalString( i ), totalsealed.getTotalString( i ) } );
523 }
524 }
525
526 sink.tableRows_();
527
528 endTable();
529 endSection();
530 }
531
532 private void tableRow( boolean fullRow, String[] content )
533 {
534 sink.tableRow();
535
536 int count = fullRow ? content.length : ( content.length - 1 );
537
538 for ( int i = 0; i < count; i++ )
539 {
540 tableCell( content[i] );
541 }
542
543 sink.tableRow_();
544 }
545
546 private void createExceptionInfoTableRow( Artifact artifact, File artifactFile, Exception e, boolean hasSealed )
547 {
548 tableRow( hasSealed, new String[] { artifact.getId(), artifactFile.getAbsolutePath(), e.getMessage(), "", "",
549 "", "", "" } );
550 }
551
552 private void populateRepositoryMap( Map repos, List rowRepos )
553 {
554 Iterator it = rowRepos.iterator();
555 while ( it.hasNext() )
556 {
557 ArtifactRepository repo = (ArtifactRepository) it.next();
558
559 repos.put( repo.getId(), repo );
560 }
561 }
562
563 private void blacklistRepositoryMap( Map repos, List repoUrlBlackListed )
564 {
565 for ( Iterator it = repos.keySet().iterator(); it.hasNext(); )
566 {
567 String key = (String) it.next();
568 ArtifactRepository repo = (ArtifactRepository) repos.get( key );
569
570
571 if ( !repo.isBlacklisted() )
572 {
573 if ( !repoUrlBlackListed.contains( repo.getUrl() ) )
574 {
575 try
576 {
577 URL repoUrl = new URL( repo.getUrl() );
578 if ( ProjectInfoReportUtils.getInputStream( repoUrl, settings ) == null )
579 {
580 log.warn( "The repository url '" + repoUrl + "' has no stream - Repository '"
581 + repo.getId() + "' will be blacklisted." );
582 repo.setBlacklisted( true );
583 repoUrlBlackListed.add( repo.getUrl() );
584 }
585 }
586 catch ( IOException e )
587 {
588 log.warn( "The repository url '" + repo.getUrl() + "' is invalid - Repository '" + repo.getId()
589 + "' will be blacklisted." );
590 repo.setBlacklisted( true );
591 repoUrlBlackListed.add( repo.getUrl() );
592 }
593 }
594 else
595 {
596 repo.setBlacklisted( true );
597 }
598 }
599 else
600 {
601 repoUrlBlackListed.add( repo.getUrl() );
602 }
603 }
604 }
605
606 private void renderSectionDependencyRepositoryLocations()
607 {
608 startSection( getReportString( "report.dependencies.repo.locations.title" ) );
609
610
611 List alldeps = dependencies.getAllDependencies();
612 Collections.sort( alldeps, getArtifactComparator() );
613
614
615 Map repoMap = new HashMap();
616
617 populateRepositoryMap( repoMap, repoUtils.getRemoteArtifactRepositories() );
618 for ( Iterator it = alldeps.iterator(); it.hasNext(); )
619 {
620 Artifact artifact = (Artifact) it.next();
621 try
622 {
623 MavenProject artifactProject = repoUtils.getMavenProjectFromRepository( artifact );
624 populateRepositoryMap( repoMap, artifactProject.getRemoteArtifactRepositories() );
625 }
626 catch ( ProjectBuildingException e )
627 {
628 log.warn( "Unable to create Maven project from repository.", e );
629 }
630 }
631
632 List repoUrlBlackListed = new ArrayList();
633 blacklistRepositoryMap( repoMap, repoUrlBlackListed );
634
635
636
637 printRepositories( repoMap, repoUrlBlackListed );
638
639
640
641 printArtifactsLocations( repoMap, alldeps );
642
643 endSection();
644 }
645
646 private void renderSectionDependencyLicenseListing()
647 {
648 startSection( getReportString( "report.dependencies.graph.tables.licenses" ) );
649 printGroupedLicenses();
650 endSection();
651 }
652
653 private void renderDependenciesForScope( String scope, List artifacts )
654 {
655 if ( artifacts != null )
656 {
657 boolean withClassifier = hasClassifier( artifacts );
658 boolean withOptional = hasOptional( artifacts );
659 String[] tableHeader = getDependencyTableHeader( withClassifier, withOptional );
660
661
662 Collections.sort( artifacts, getArtifactComparator() );
663
664 startSection( scope );
665
666 paragraph( getReportString( "report.dependencies.intro." + scope ) );
667
668 startTable();
669 tableHeader( tableHeader );
670 for ( Iterator iterator = artifacts.iterator(); iterator.hasNext(); )
671 {
672 Artifact artifact = (Artifact) iterator.next();
673
674 renderArtifactRow( artifact, withClassifier, withOptional );
675 }
676 endTable();
677
678 endSection();
679 }
680 }
681
682 private Comparator getArtifactComparator()
683 {
684 return new Comparator()
685 {
686 public int compare( Object o1, Object o2 )
687 {
688 Artifact a1 = (Artifact) o1;
689 Artifact a2 = (Artifact) o2;
690
691
692 if ( a1.isOptional() && !a2.isOptional() )
693 {
694 return +1;
695 }
696 else if ( !a1.isOptional() && a2.isOptional() )
697 {
698 return -1;
699 }
700 else
701 {
702 return a1.compareTo( a2 );
703 }
704 }
705 };
706 }
707
708
709
710
711
712
713
714 private void renderArtifactRow( Artifact artifact, boolean withClassifier, boolean withOptional )
715 {
716 String isOptional =
717 artifact.isOptional() ? getReportString( "report.dependencies.column.isOptional" )
718 : getReportString( "report.dependencies.column.isNotOptional" );
719
720 String url =
721 ProjectInfoReportUtils.getArtifactUrl( artifactFactory, artifact, mavenProjectBuilder, remoteRepositories,
722 localRepository );
723 String artifactIdCell = ProjectInfoReportUtils.getArtifactIdCell( artifact.getArtifactId(), url );
724
725 String content[];
726 if ( withClassifier )
727 {
728 content =
729 new String[] { artifact.getGroupId(), artifactIdCell, artifact.getVersion(), artifact.getClassifier(),
730 artifact.getType(), isOptional };
731 }
732 else
733 {
734 content =
735 new String[] { artifact.getGroupId(), artifactIdCell, artifact.getVersion(), artifact.getType(),
736 isOptional };
737 }
738
739 tableRow( withOptional, content );
740 }
741
742 private void printDependencyListing( DependencyNode node )
743 {
744 Artifact artifact = node.getArtifact();
745 String id = artifact.getId();
746 String dependencyDetailId = getUUID();
747 String imgId = getUUID();
748
749 sink.listItem();
750
751 sink.paragraph();
752 sink.text( id + ( StringUtils.isNotEmpty( artifact.getScope() ) ? " (" + artifact.getScope() + ") " : " " ) );
753 sink.rawText( "<img id=\"" + imgId + "\" src=\"" + IMG_INFO_URL
754 + "\" alt=\"Information\" onclick=\"toggleDependencyDetail( '" + dependencyDetailId + "', '" + imgId
755 + "' );\" style=\"cursor: pointer;vertical-align:text-bottom;\"></img>" );
756 sink.paragraph_();
757
758 printDescriptionsAndURLs( node, dependencyDetailId );
759
760 if ( !node.getChildren().isEmpty() )
761 {
762 boolean toBeIncluded = false;
763 List subList = new ArrayList();
764 for ( Iterator deps = node.getChildren().iterator(); deps.hasNext(); )
765 {
766 DependencyNode dep = (DependencyNode) deps.next();
767
768 if ( !dependencies.getAllDependencies().contains( dep.getArtifact() ) )
769 {
770 continue;
771 }
772
773 subList.add( dep );
774 toBeIncluded = true;
775 }
776
777 if ( toBeIncluded )
778 {
779 sink.list();
780 for ( Iterator deps = subList.iterator(); deps.hasNext(); )
781 {
782 DependencyNode dep = (DependencyNode) deps.next();
783
784 printDependencyListing( dep );
785 }
786 sink.list_();
787 }
788 }
789
790 sink.listItem_();
791 }
792
793 private void printDescriptionsAndURLs( DependencyNode node, String uid )
794 {
795 Artifact artifact = node.getArtifact();
796 String id = artifact.getId();
797 String unknownLicenseMessage = getReportString( "report.dependencies.graph.tables.unknown" );
798
799 sink.rawText( "<div id=\"" + uid + "\" style=\"display:none\">" );
800
801 sink.table();
802
803 if ( !Artifact.SCOPE_SYSTEM.equals( artifact.getScope() ) )
804 {
805 try
806 {
807 MavenProject artifactProject = repoUtils.getMavenProjectFromRepository( artifact );
808 String artifactDescription = artifactProject.getDescription();
809 String artifactUrl = artifactProject.getUrl();
810 String artifactName = artifactProject.getName();
811 List licenses = artifactProject.getLicenses();
812
813 sink.tableRow();
814 sink.tableHeaderCell();
815 sink.text( artifactName );
816 sink.tableHeaderCell_();
817 sink.tableRow_();
818
819 sink.tableRow();
820 sink.tableCell();
821
822 sink.paragraph();
823 sink.bold();
824 sink.text( getReportString( "report.dependencies.column.description" ) + ": " );
825 sink.bold_();
826 if ( StringUtils.isNotEmpty( artifactDescription ) )
827 {
828 sink.text( artifactDescription );
829 }
830 else
831 {
832 sink.text( getReportString( "report.index.nodescription" ) );
833 }
834 sink.paragraph_();
835
836 if ( StringUtils.isNotEmpty( artifactUrl ) )
837 {
838 sink.paragraph();
839 sink.bold();
840 sink.text( getReportString( "report.dependencies.column.url" ) + ": " );
841 sink.bold_();
842 if ( ProjectInfoReportUtils.isArtifactUrlValid( artifactUrl ) )
843 {
844 sink.link( artifactUrl );
845 sink.text( artifactUrl );
846 sink.link_();
847 }
848 else
849 {
850 sink.text( artifactUrl );
851 }
852 sink.paragraph_();
853 }
854
855 sink.paragraph();
856 sink.bold();
857 sink.text( getReportString( "report.license.title" ) + ": " );
858 sink.bold_();
859 if ( !licenses.isEmpty() )
860 {
861 for ( Iterator iter = licenses.iterator(); iter.hasNext(); )
862 {
863 License element = (License) iter.next();
864 String licenseName = element.getName();
865 String licenseUrl = element.getUrl();
866
867 if ( licenseUrl != null )
868 {
869 sink.link( licenseUrl );
870 }
871 sink.text( licenseName );
872
873 if ( licenseUrl != null )
874 {
875 sink.link_();
876 }
877
878 licenseMap.put( licenseName, artifactName );
879 }
880 }
881 else
882 {
883 sink.text( getReportString( "report.license.nolicense" ) );
884
885 licenseMap.put( unknownLicenseMessage, artifactName );
886 }
887 sink.paragraph_();
888 }
889 catch ( ProjectBuildingException e )
890 {
891 log.error( "ProjectBuildingException error : ", e );
892 }
893 }
894 else
895 {
896 sink.tableRow();
897 sink.tableHeaderCell();
898 sink.text( id );
899 sink.tableHeaderCell_();
900 sink.tableRow_();
901
902 sink.tableRow();
903 sink.tableCell();
904
905 sink.paragraph();
906 sink.bold();
907 sink.text( getReportString( "report.dependencies.column.description" ) + ": " );
908 sink.bold_();
909 sink.text( getReportString( "report.index.nodescription" ) );
910 sink.paragraph_();
911
912 if ( artifact.getFile() != null )
913 {
914 sink.paragraph();
915 sink.bold();
916 sink.text( getReportString( "report.dependencies.column.url" ) + ": " );
917 sink.bold_();
918 sink.text( artifact.getFile().getAbsolutePath() );
919 sink.paragraph_();
920 }
921 }
922
923 sink.tableCell_();
924 sink.tableRow_();
925
926 sink.table_();
927
928 sink.rawText( "</div>" );
929 }
930
931 private void printGroupedLicenses()
932 {
933 for ( Iterator iter = licenseMap.keySet().iterator(); iter.hasNext(); )
934 {
935 String licenseName = (String) iter.next();
936 sink.paragraph();
937 sink.bold();
938 if ( StringUtils.isEmpty( licenseName ) )
939 {
940 sink.text( i18n.getString( "project-info-report", locale, "report.dependencies.unamed" ) );
941 }
942 else
943 {
944 sink.text( licenseName );
945 }
946 sink.text( ": " );
947 sink.bold_();
948
949 SortedSet projects = (SortedSet) licenseMap.get( licenseName );
950
951 for ( Iterator iterator = projects.iterator(); iterator.hasNext(); )
952 {
953 String projectName = (String) iterator.next();
954 sink.text( projectName );
955 if ( iterator.hasNext() )
956 {
957 sink.text( ", " );
958 }
959 }
960
961 sink.paragraph_();
962 }
963 }
964
965 private void printRepositories( Map repoMap, List repoUrlBlackListed )
966 {
967
968 String repoid = getReportString( "report.dependencies.repo.locations.column.repoid" );
969 String url = getReportString( "report.dependencies.repo.locations.column.url" );
970 String release = getReportString( "report.dependencies.repo.locations.column.release" );
971 String snapshot = getReportString( "report.dependencies.repo.locations.column.snapshot" );
972 String blacklisted = getReportString( "report.dependencies.repo.locations.column.blacklisted" );
973 String releaseEnabled = getReportString( "report.dependencies.repo.locations.cell.release.enabled" );
974 String releaseDisabled = getReportString( "report.dependencies.repo.locations.cell.release.disabled" );
975 String snapshotEnabled = getReportString( "report.dependencies.repo.locations.cell.snapshot.enabled" );
976 String snapshotDisabled = getReportString( "report.dependencies.repo.locations.cell.snapshot.disabled" );
977 String blacklistedEnabled = getReportString( "report.dependencies.repo.locations.cell.blacklisted.enabled" );
978 String blacklistedDisabled = getReportString( "report.dependencies.repo.locations.cell.blacklisted.disabled" );
979
980 startTable();
981
982
983
984 String[] tableHeader;
985 int[] justificationRepo;
986 if ( repoUrlBlackListed.isEmpty() )
987 {
988 tableHeader = new String[] { repoid, url, release, snapshot };
989 justificationRepo =
990 new int[] { Parser.JUSTIFY_LEFT, Parser.JUSTIFY_LEFT, Parser.JUSTIFY_CENTER, Parser.JUSTIFY_CENTER };
991 }
992 else
993 {
994 tableHeader = new String[] { repoid, url, release, snapshot, blacklisted };
995 justificationRepo =
996 new int[] { Parser.JUSTIFY_LEFT, Parser.JUSTIFY_LEFT, Parser.JUSTIFY_CENTER, Parser.JUSTIFY_CENTER,
997 Parser.JUSTIFY_CENTER };
998 }
999
1000 sink.tableRows( justificationRepo, true );
1001
1002 tableHeader( tableHeader );
1003
1004
1005
1006 for ( Iterator it = repoMap.keySet().iterator(); it.hasNext(); )
1007 {
1008 String key = (String) it.next();
1009 ArtifactRepository repo = (ArtifactRepository) repoMap.get( key );
1010
1011 sink.tableRow();
1012 tableCell( repo.getId() );
1013
1014 sink.tableCell();
1015 if ( repo.isBlacklisted() )
1016 {
1017 sink.text( repo.getUrl() );
1018 }
1019 else
1020 {
1021 sink.link( repo.getUrl() );
1022 sink.text( repo.getUrl() );
1023 sink.link_();
1024 }
1025 sink.tableCell_();
1026
1027 ArtifactRepositoryPolicy releasePolicy = repo.getReleases();
1028 tableCell( releasePolicy.isEnabled() ? releaseEnabled : releaseDisabled );
1029
1030 ArtifactRepositoryPolicy snapshotPolicy = repo.getSnapshots();
1031 tableCell( snapshotPolicy.isEnabled() ? snapshotEnabled : snapshotDisabled );
1032
1033 if ( !repoUrlBlackListed.isEmpty() )
1034 {
1035 tableCell( repo.isBlacklisted() ? blacklistedEnabled : blacklistedDisabled );
1036 }
1037 sink.tableRow_();
1038 }
1039
1040 sink.tableRows_();
1041
1042 endTable();
1043 }
1044
1045 private void printArtifactsLocations( Map repoMap, List alldeps )
1046 {
1047
1048 String artifact = getReportString( "report.dependencies.repo.locations.column.artifact" );
1049
1050 sink.paragraph();
1051 sink.text( getReportString( "report.dependencies.repo.locations.artifact.breakdown" ) );
1052 sink.paragraph_();
1053
1054 List repoIdList = new ArrayList();
1055
1056 for ( Iterator it = repoMap.keySet().iterator(); it.hasNext(); )
1057 {
1058 String repokey = (String) it.next();
1059 ArtifactRepository repo = (ArtifactRepository) repoMap.get( repokey );
1060 if ( !repo.isBlacklisted() )
1061 {
1062 repoIdList.add( repokey );
1063 }
1064 }
1065
1066 String[] tableHeader = new String[repoIdList.size() + 1];
1067 int[] justificationRepo = new int[repoIdList.size() + 1];
1068
1069 tableHeader[0] = artifact;
1070 justificationRepo[0] = Parser.JUSTIFY_LEFT;
1071
1072 int idnum = 1;
1073 for ( Iterator it = repoIdList.iterator(); it.hasNext(); )
1074 {
1075 String id = (String) it.next();
1076 tableHeader[idnum] = id;
1077 justificationRepo[idnum] = Parser.JUSTIFY_CENTER;
1078 idnum++;
1079 }
1080
1081 Map totalByRepo = new HashMap();
1082 TotalCell totaldeps = new TotalCell( DEFAULT_DECIMAL_FORMAT );
1083
1084 startTable();
1085
1086 sink.tableRows( justificationRepo, true );
1087
1088 tableHeader( tableHeader );
1089
1090 for ( Iterator it = alldeps.iterator(); it.hasNext(); )
1091 {
1092 Artifact dependency = (Artifact) it.next();
1093
1094 totaldeps.incrementTotal( dependency.getScope() );
1095
1096 sink.tableRow();
1097
1098 if ( !Artifact.SCOPE_SYSTEM.equals( dependency.getScope() ) )
1099 {
1100
1101 tableCell( dependency.getId() );
1102
1103 for ( Iterator itrepo = repoIdList.iterator(); itrepo.hasNext(); )
1104 {
1105 String repokey = (String) itrepo.next();
1106 ArtifactRepository repo = (ArtifactRepository) repoMap.get( repokey );
1107
1108 String depUrl = repoUtils.getDependencyUrlFromRepository( dependency, repo );
1109
1110 Integer old = (Integer) totalByRepo.get( repokey );
1111 if ( old == null )
1112 {
1113 totalByRepo.put( repokey, new Integer( 0 ) );
1114 old = new Integer( 0 );
1115 }
1116
1117 boolean dependencyExists = false;
1118
1119 if ( ( dependency.isSnapshot() && repo.getSnapshots().isEnabled() )
1120 || ( !dependency.isSnapshot() && repo.getReleases().isEnabled() ) )
1121 {
1122 dependencyExists = repoUtils.dependencyExistsInRepo( repo, dependency );
1123 }
1124
1125 if ( dependencyExists )
1126 {
1127 sink.tableCell();
1128 if ( StringUtils.isNotEmpty( depUrl ) )
1129 {
1130 sink.link( depUrl );
1131 }
1132 else
1133 {
1134 sink.text( depUrl );
1135 }
1136
1137 sink.figure();
1138 sink.figureCaption();
1139 sink.text( "Found at " + repo.getUrl() );
1140 sink.figureCaption_();
1141 sink.figureGraphics( "images/icon_success_sml.gif" );
1142 sink.figure_();
1143
1144 sink.link_();
1145 sink.tableCell_();
1146
1147 totalByRepo.put( repokey, new Integer( old.intValue() + 1 ) );
1148 }
1149 else
1150 {
1151 tableCell( "-" );
1152 }
1153 }
1154 }
1155 else
1156 {
1157 tableCell( dependency.getId() );
1158
1159 for ( Iterator itrepo = repoIdList.iterator(); itrepo.hasNext(); )
1160 {
1161 itrepo.next();
1162
1163 tableCell( "-" );
1164 }
1165 }
1166
1167 sink.tableRow_();
1168 }
1169
1170
1171
1172
1173 tableHeader[0] = getReportString( "report.dependencies.file.details.total" );
1174 tableHeader( tableHeader );
1175 String[] totalRow = new String[repoIdList.size() + 1];
1176 totalRow[0] = totaldeps.toString();
1177 idnum = 1;
1178 for ( Iterator itrepo = repoIdList.iterator(); itrepo.hasNext(); )
1179 {
1180 String repokey = (String) itrepo.next();
1181
1182 totalRow[idnum++] = totalByRepo.get( repokey ).toString();
1183 }
1184
1185 tableRow( totalRow );
1186
1187 sink.tableRows_();
1188
1189 endTable();
1190 }
1191
1192 private String getReportString( String key )
1193 {
1194 return i18n.getString( "project-info-report", locale, key );
1195 }
1196
1197
1198
1199
1200
1201 private boolean hasClassifier( List artifacts )
1202 {
1203 for ( Iterator iterator = artifacts.iterator(); iterator.hasNext(); )
1204 {
1205 Artifact artifact = (Artifact) iterator.next();
1206
1207 if ( StringUtils.isNotEmpty( artifact.getClassifier() ) )
1208 {
1209 return true;
1210 }
1211 }
1212
1213 return false;
1214 }
1215
1216
1217
1218
1219
1220 private boolean hasOptional( List artifacts )
1221 {
1222 for ( Iterator iterator = artifacts.iterator(); iterator.hasNext(); )
1223 {
1224 Artifact artifact = (Artifact) iterator.next();
1225
1226 if ( artifact.isOptional() )
1227 {
1228 return true;
1229 }
1230 }
1231
1232 return false;
1233 }
1234
1235
1236
1237
1238
1239 private boolean hasSealed( List artifacts )
1240 {
1241 for ( Iterator it = artifacts.iterator(); it.hasNext(); )
1242 {
1243 Artifact artifact = (Artifact) it.next();
1244
1245
1246 if ( artifact.getFile() == null && !Artifact.SCOPE_SYSTEM.equals( artifact.getScope() ) )
1247 {
1248 try
1249 {
1250 repoUtils.resolve( artifact );
1251 }
1252 catch ( ArtifactResolutionException e )
1253 {
1254 log.error( "Artifact: " + artifact.getId() + " has no file.", e );
1255 continue;
1256 }
1257 catch ( ArtifactNotFoundException e )
1258 {
1259 if ( ( dependencies.getProject().getGroupId().equals( artifact.getGroupId() ) )
1260 && ( dependencies.getProject().getArtifactId().equals( artifact.getArtifactId() ) )
1261 && ( dependencies.getProject().getVersion().equals( artifact.getVersion() ) ) )
1262 {
1263 log.warn( "The artifact of this project has never been deployed." );
1264 }
1265 else
1266 {
1267 log.error( "Artifact: " + artifact.getId() + " has no file.", e );
1268 }
1269
1270 continue;
1271 }
1272 }
1273
1274 if ( JAR_SUBTYPE.contains( artifact.getType().toLowerCase() ) )
1275 {
1276 try
1277 {
1278 JarData jarDetails = dependencies.getJarDependencyDetails( artifact );
1279 if ( jarDetails.isSealed() )
1280 {
1281 return true;
1282 }
1283 }
1284 catch ( IOException e )
1285 {
1286 log.error( "IOException: " + e.getMessage(), e );
1287 }
1288 }
1289 }
1290 return false;
1291 }
1292
1293
1294
1295
1296
1297 private static String getUUID()
1298 {
1299 return "_" + Math.abs( RANDOM.nextInt() );
1300 }
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313 static class FileDecimalFormat
1314 extends DecimalFormat
1315 {
1316 private static final long serialVersionUID = 4062503546523610081L;
1317
1318 private final I18N i18n;
1319
1320 private final Locale locale;
1321
1322
1323
1324
1325
1326
1327
1328 public FileDecimalFormat( I18N i18n, Locale locale )
1329 {
1330 super( "#,###.00" );
1331
1332 this.i18n = i18n;
1333 this.locale = locale;
1334 }
1335
1336
1337 public StringBuffer format( long fs, StringBuffer result, FieldPosition fieldPosition )
1338 {
1339 if ( fs > 1024 * 1024 * 1024 )
1340 {
1341 result = super.format( (float) fs / ( 1024 * 1024 * 1024 ), result, fieldPosition );
1342 result.append( " " ).append( getString( i18n, "report.dependencies.file.details.column.size.gb" ) );
1343 return result;
1344 }
1345
1346 if ( fs > 1024 * 1024 )
1347 {
1348 result = super.format( (float) fs / ( 1024 * 1024 ), result, fieldPosition );
1349 result.append( " " ).append( getString( i18n, "report.dependencies.file.details.column.size.mb" ) );
1350 return result;
1351 }
1352
1353 result = super.format( (float) fs / ( 1024 ), result, fieldPosition );
1354 result.append( " " ).append( getString( i18n, "report.dependencies.file.details.column.size.kb" ) );
1355 return result;
1356 }
1357
1358 private String getString( I18N i18n, String key )
1359 {
1360 return i18n.getString( "project-info-report", locale, key );
1361 }
1362 }
1363
1364
1365
1366
1367 static class TotalCell
1368 {
1369 static final int SCOPES_COUNT = 5;
1370
1371 final DecimalFormat decimalFormat;
1372
1373 long total = 0;
1374
1375 long totalCompileScope = 0;
1376
1377 long totalTestScope = 0;
1378
1379 long totalRuntimeScope = 0;
1380
1381 long totalProvidedScope = 0;
1382
1383 long totalSystemScope = 0;
1384
1385 TotalCell( DecimalFormat decimalFormat )
1386 {
1387 this.decimalFormat = decimalFormat;
1388 }
1389
1390 void incrementTotal( String scope )
1391 {
1392 addTotal( 1, scope );
1393 }
1394
1395 static String getScope( int index )
1396 {
1397 switch ( index )
1398 {
1399 case 0:
1400 return Artifact.SCOPE_COMPILE;
1401 case 1:
1402 return Artifact.SCOPE_TEST;
1403 case 2:
1404 return Artifact.SCOPE_RUNTIME;
1405 case 3:
1406 return Artifact.SCOPE_PROVIDED;
1407 case 4:
1408 return Artifact.SCOPE_SYSTEM;
1409 default:
1410 return null;
1411 }
1412 }
1413
1414 long getTotal( int index )
1415 {
1416 switch ( index )
1417 {
1418 case 0:
1419 return totalCompileScope;
1420 case 1:
1421 return totalTestScope;
1422 case 2:
1423 return totalRuntimeScope;
1424 case 3:
1425 return totalProvidedScope;
1426 case 4:
1427 return totalSystemScope;
1428 default:
1429 return total;
1430 }
1431 }
1432
1433 String getTotalString( int index )
1434 {
1435 long total = getTotal( index );
1436
1437 if ( total <= 0 )
1438 {
1439 return "";
1440 }
1441
1442 StringBuffer sb = new StringBuffer();
1443 if ( index >= 0 )
1444 {
1445 sb.append( getScope( index ) ).append( ": " );
1446 }
1447 sb.append( decimalFormat.format( getTotal( index ) ) );
1448 return sb.toString();
1449 }
1450
1451 void addTotal( long add, String scope )
1452 {
1453 total += add;
1454
1455 if ( Artifact.SCOPE_COMPILE.equals( scope ) )
1456 {
1457 totalCompileScope += add;
1458 }
1459 else if ( Artifact.SCOPE_TEST.equals( scope ) )
1460 {
1461 totalTestScope += add;
1462 }
1463 else if ( Artifact.SCOPE_RUNTIME.equals( scope ) )
1464 {
1465 totalRuntimeScope += add;
1466 }
1467 else if ( Artifact.SCOPE_PROVIDED.equals( scope ) )
1468 {
1469 totalProvidedScope += add;
1470 }
1471 else if ( Artifact.SCOPE_SYSTEM.equals( scope ) )
1472 {
1473 totalSystemScope += add;
1474 }
1475 }
1476
1477
1478 public String toString()
1479 {
1480 StringBuffer sb = new StringBuffer();
1481 sb.append( decimalFormat.format( total ) );
1482 sb.append( " (" );
1483
1484 boolean needSeparator = false;
1485 for ( int i = 0; i < SCOPES_COUNT; i++ )
1486 {
1487 if ( getTotal( i ) > 0 )
1488 {
1489 if ( needSeparator )
1490 {
1491 sb.append( ", " );
1492 }
1493 sb.append( getTotalString( i ) );
1494 needSeparator = true;
1495 }
1496 }
1497
1498 sb.append( ")" );
1499
1500 return sb.toString();
1501 }
1502 }
1503 }