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