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