1 package org.apache.maven.doxia.tools;
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.io.InputStream;
25 import java.io.Reader;
26 import java.io.StringReader;
27 import java.net.MalformedURLException;
28 import java.net.URL;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.Collections;
32 import java.util.HashMap;
33 import java.util.Iterator;
34 import java.util.List;
35 import java.util.Locale;
36 import java.util.Map;
37 import java.util.StringTokenizer;
38
39 import org.apache.commons.io.FilenameUtils;
40 import org.apache.maven.artifact.Artifact;
41 import org.apache.maven.artifact.factory.ArtifactFactory;
42 import org.apache.maven.artifact.repository.ArtifactRepository;
43 import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
44 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
45 import org.apache.maven.artifact.resolver.ArtifactResolver;
46 import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
47 import org.apache.maven.artifact.versioning.VersionRange;
48 import org.apache.maven.doxia.site.decoration.Banner;
49 import org.apache.maven.doxia.site.decoration.DecorationModel;
50 import org.apache.maven.doxia.site.decoration.Menu;
51 import org.apache.maven.doxia.site.decoration.MenuItem;
52 import org.apache.maven.doxia.site.decoration.Skin;
53 import org.apache.maven.doxia.site.decoration.inheritance.DecorationModelInheritanceAssembler;
54 import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Reader;
55 import org.apache.maven.model.DistributionManagement;
56 import org.apache.maven.model.Model;
57 import org.apache.maven.model.Site;
58 import org.apache.maven.project.MavenProject;
59 import org.apache.maven.project.MavenProjectBuilder;
60 import org.apache.maven.project.ProjectBuildingException;
61 import org.apache.maven.reporting.MavenReport;
62 import org.codehaus.plexus.component.annotations.Component;
63 import org.codehaus.plexus.component.annotations.Requirement;
64 import org.codehaus.plexus.i18n.I18N;
65 import org.codehaus.plexus.logging.AbstractLogEnabled;
66 import org.codehaus.plexus.util.IOUtil;
67 import org.codehaus.plexus.util.ReaderFactory;
68 import org.codehaus.plexus.util.StringUtils;
69 import org.codehaus.plexus.interpolation.EnvarBasedValueSource;
70 import org.codehaus.plexus.interpolation.InterpolationException;
71 import org.codehaus.plexus.interpolation.MapBasedValueSource;
72 import org.codehaus.plexus.interpolation.ObjectBasedValueSource;
73 import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
74 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
75
76
77
78
79
80
81
82 @Component( role = SiteTool.class )
83 public class DefaultSiteTool
84 extends AbstractLogEnabled
85 implements SiteTool
86 {
87
88
89
90
91
92
93
94 @Requirement
95 private ArtifactResolver artifactResolver;
96
97
98
99
100 @Requirement
101 private ArtifactFactory artifactFactory;
102
103
104
105
106 @Requirement
107 protected I18N i18n;
108
109
110
111
112 @Requirement
113 protected DecorationModelInheritanceAssembler assembler;
114
115
116
117
118 @Requirement
119 protected MavenProjectBuilder mavenProjectBuilder;
120
121
122
123
124
125
126 public Artifact getSkinArtifactFromRepository( ArtifactRepository localRepository,
127 List<ArtifactRepository> remoteArtifactRepositories,
128 DecorationModel decoration )
129 throws SiteToolException
130 {
131 checkNotNull( "localRepository", localRepository );
132 checkNotNull( "remoteArtifactRepositories", remoteArtifactRepositories );
133 checkNotNull( "decoration", decoration );
134
135 Skin skin = decoration.getSkin();
136
137 if ( skin == null )
138 {
139 skin = Skin.getDefaultSkin();
140 }
141
142 String version = skin.getVersion();
143 Artifact artifact;
144 try
145 {
146 if ( version == null )
147 {
148 version = Artifact.RELEASE_VERSION;
149 }
150 VersionRange versionSpec = VersionRange.createFromVersionSpec( version );
151 artifact = artifactFactory.createDependencyArtifact( skin.getGroupId(), skin.getArtifactId(), versionSpec,
152 "jar", null, null );
153
154 artifactResolver.resolve( artifact, remoteArtifactRepositories, localRepository );
155 }
156 catch ( InvalidVersionSpecificationException e )
157 {
158 throw new SiteToolException( "InvalidVersionSpecificationException: The skin version '" + version
159 + "' is not valid: " + e.getMessage(), e );
160 }
161 catch ( ArtifactResolutionException e )
162 {
163 throw new SiteToolException( "ArtifactResolutionException: Unable to find skin", e );
164 }
165 catch ( ArtifactNotFoundException e )
166 {
167 throw new SiteToolException( "ArtifactNotFoundException: The skin does not exist: " + e.getMessage(), e );
168 }
169
170 return artifact;
171 }
172
173
174 public Artifact getDefaultSkinArtifact( ArtifactRepository localRepository,
175 List<ArtifactRepository> remoteArtifactRepositories )
176 throws SiteToolException
177 {
178 return getSkinArtifactFromRepository( localRepository, remoteArtifactRepositories, new DecorationModel() );
179 }
180
181
182 public String getRelativePath( String to, String from )
183 {
184 checkNotNull( "to", to );
185 checkNotNull( "from", from );
186
187 URL toUrl = null;
188 URL fromUrl = null;
189
190 String toPath = to;
191 String fromPath = from;
192
193 try
194 {
195 toUrl = new URL( to );
196 }
197 catch ( MalformedURLException e )
198 {
199 try
200 {
201 toUrl = new File( getNormalizedPath( to ) ).toURI().toURL();
202 }
203 catch ( MalformedURLException e1 )
204 {
205 getLogger().warn( "Unable to load a URL for '" + to + "': " + e.getMessage() );
206 }
207 }
208
209 try
210 {
211 fromUrl = new URL( from );
212 }
213 catch ( MalformedURLException e )
214 {
215 try
216 {
217 fromUrl = new File( getNormalizedPath( from ) ).toURI().toURL();
218 }
219 catch ( MalformedURLException e1 )
220 {
221 getLogger().warn( "Unable to load a URL for '" + from + "': " + e.getMessage() );
222 }
223 }
224
225 if ( toUrl != null && fromUrl != null )
226 {
227
228
229 if ( ( toUrl.getProtocol().equalsIgnoreCase( fromUrl.getProtocol() ) )
230 && ( toUrl.getHost().equalsIgnoreCase( fromUrl.getHost() ) )
231 && ( toUrl.getPort() == fromUrl.getPort() ) )
232 {
233
234
235 toPath = toUrl.getFile();
236 fromPath = fromUrl.getFile();
237 }
238 else
239 {
240
241
242 return to;
243 }
244 }
245 else if ( ( toUrl != null && fromUrl == null ) || ( toUrl == null && fromUrl != null ) )
246 {
247
248
249 return to;
250 }
251
252
253
254
255
256 String relativePath = getRelativeFilePath( fromPath, toPath );
257
258 if ( relativePath == null )
259 {
260 relativePath = to;
261 }
262
263 if ( getLogger().isDebugEnabled() && !relativePath.toString().equals( to ) )
264 {
265 getLogger().debug( "Mapped url: " + to + " to relative path: " + relativePath );
266 }
267
268 return relativePath;
269 }
270
271 private static String getRelativeFilePath( final String oldPath, final String newPath )
272 {
273
274
275 String fromPath = new File( oldPath ).getPath();
276 String toPath = new File( newPath ).getPath();
277
278
279 if ( toPath.matches( "^\\[a-zA-Z]:" ) )
280 {
281 toPath = toPath.substring( 1 );
282 }
283 if ( fromPath.matches( "^\\[a-zA-Z]:" ) )
284 {
285 fromPath = fromPath.substring( 1 );
286 }
287
288
289 if ( fromPath.startsWith( ":", 1 ) )
290 {
291 fromPath = Character.toLowerCase( fromPath.charAt( 0 ) ) + fromPath.substring( 1 );
292 }
293 if ( toPath.startsWith( ":", 1 ) )
294 {
295 toPath = Character.toLowerCase( toPath.charAt( 0 ) ) + toPath.substring( 1 );
296 }
297
298
299
300
301 if ( ( toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) )
302 && ( !toPath.substring( 0, 1 ).equals( fromPath.substring( 0, 1 ) ) ) )
303 {
304
305
306
307 return null;
308 }
309
310 if ( ( toPath.startsWith( ":", 1 ) && !fromPath.startsWith( ":", 1 ) )
311 || ( !toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) )
312 {
313
314
315
316
317 return null;
318
319 }
320
321 final String relativePath = buildRelativePath( toPath, fromPath, File.separatorChar );
322
323 return relativePath.toString();
324 }
325
326
327 public File getSiteDescriptorFromBasedir( String siteDirectory, File basedir, Locale locale )
328 {
329 checkNotNull( "basedir", basedir );
330
331 String dir = siteDirectory;
332 if ( dir == null )
333 {
334
335 dir = "src/site";
336 }
337
338 final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale;
339
340 File siteDir = new File( basedir, dir );
341
342 File siteDescriptor = new File( siteDir, "site_" + llocale.getLanguage() + ".xml" );
343
344 if ( !siteDescriptor.isFile() )
345 {
346 siteDescriptor = new File( siteDir, "site.xml" );
347 }
348 return siteDescriptor;
349 }
350
351
352 public File getSiteDescriptorFromRepository( MavenProject project, ArtifactRepository localRepository,
353 List<ArtifactRepository> repositories, Locale locale )
354 throws SiteToolException
355 {
356 checkNotNull( "project", project );
357 checkNotNull( "localRepository", localRepository );
358 checkNotNull( "repositories", repositories );
359
360 final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale;
361
362 try
363 {
364 return resolveSiteDescriptor( project, localRepository, repositories, llocale );
365 }
366 catch ( ArtifactNotFoundException e )
367 {
368 getLogger().debug( "ArtifactNotFoundException: Unable to locate site descriptor: " + e );
369 return null;
370 }
371 catch ( ArtifactResolutionException e )
372 {
373 throw new SiteToolException( "ArtifactResolutionException: Unable to locate site descriptor: "
374 + e.getMessage(), e );
375 }
376 catch ( IOException e )
377 {
378 throw new SiteToolException( "IOException: Unable to locate site descriptor: " + e.getMessage(), e );
379 }
380 }
381
382
383 public DecorationModel getDecorationModel( MavenProject project, List<MavenProject> reactorProjects,
384 ArtifactRepository localRepository,
385 List<ArtifactRepository> repositories, String siteDirectory,
386 Locale locale )
387 throws SiteToolException
388 {
389 checkNotNull( "project", project );
390 checkNotNull( "reactorProjects", reactorProjects );
391 checkNotNull( "localRepository", localRepository );
392 checkNotNull( "repositories", repositories );
393
394 final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale;
395
396 Map<String, String> props = new HashMap<String, String>( 2 );
397
398
399 props.put( "reports", "<menu ref=\"reports\"/>\n" );
400 props.put( "modules", "<menu ref=\"modules\"/>\n" );
401
402 MavenProject parentProject = getParentProject( project, reactorProjects, localRepository );
403
404 DecorationModel decorationModel =
405 getDecorationModel( project, parentProject, reactorProjects, localRepository, repositories, siteDirectory,
406 llocale, props );
407
408 if ( decorationModel == null )
409 {
410 String siteDescriptorContent;
411
412 InputStream in = null;
413 try
414 {
415
416 in = getClass().getResourceAsStream( "/default-site.xml" );
417 siteDescriptorContent = IOUtil.toString( in, "UTF-8" );
418 }
419 catch ( IOException e )
420 {
421 throw new SiteToolException( "Error reading default site descriptor: " + e.getMessage(), e );
422 }
423 finally
424 {
425 IOUtil.close( in );
426 }
427
428 siteDescriptorContent = getInterpolatedSiteDescriptorContent( props, project, siteDescriptorContent );
429
430 decorationModel = readDecorationModel( siteDescriptorContent );
431 }
432
433 if ( parentProject != null )
434 {
435 populateParentMenu( decorationModel, llocale, project, parentProject, true );
436 }
437
438 populateModulesMenu( project, reactorProjects, localRepository, decorationModel, llocale, true );
439
440 if ( decorationModel.getBannerLeft() == null )
441 {
442
443 Banner banner = new Banner();
444 banner.setName( project.getName() );
445 decorationModel.setBannerLeft( banner );
446 }
447
448 return decorationModel;
449 }
450
451
452 public void populateReportsMenu( DecorationModel decorationModel, Locale locale,
453 Map<String, List<MavenReport>> categories )
454 {
455 checkNotNull( "decorationModel", decorationModel );
456 checkNotNull( "categories", categories );
457
458 Menu menu = decorationModel.getMenuRef( "reports" );
459
460 if ( menu == null )
461 {
462 return;
463 }
464
465 final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale;
466
467 if ( menu.getName() == null )
468 {
469 menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectdocumentation" ) );
470 }
471
472 boolean found = false;
473 if ( menu.getItems().isEmpty() )
474 {
475 List<MavenReport> categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_INFORMATION );
476 if ( !isEmptyList( categoryReports ) )
477 {
478 MenuItem item =
479 createCategoryMenu( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectinformation" ),
480 "/project-info.html", categoryReports, llocale );
481 menu.getItems().add( item );
482 found = true;
483 }
484
485 categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_REPORTS );
486 if ( !isEmptyList( categoryReports ) )
487 {
488 MenuItem item =
489 createCategoryMenu( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectreports" ),
490 "/project-reports.html", categoryReports, llocale );
491 menu.getItems().add( item );
492 found = true;
493 }
494 }
495 if ( !found )
496 {
497 decorationModel.removeMenuRef( "reports" );
498 }
499 }
500
501
502 public String getInterpolatedSiteDescriptorContent( Map<String, String> props, MavenProject aProject,
503 String siteDescriptorContent )
504 throws SiteToolException
505 {
506 checkNotNull( "props", props );
507 checkNotNull( "aProject", aProject );
508 checkNotNull( "siteDescriptorContent", siteDescriptorContent );
509
510
511
512
513
514 Map<String, String> modulesProps = new HashMap<String, String>( 1 );
515
516
517 modulesProps.put( "modules", "<menu ref=\"modules\"/>" );
518
519 String interpolatedSiteDescriptorContent = StringUtils.interpolate( siteDescriptorContent, modulesProps );
520
521 RegexBasedInterpolator interpolator = new RegexBasedInterpolator();
522
523 try
524 {
525 interpolator.addValueSource( new EnvarBasedValueSource() );
526 }
527 catch ( IOException e )
528 {
529
530 throw new SiteToolException( "IOException: cannot interpolate environment properties: " + e.getMessage(),
531 e );
532 }
533
534 interpolator.addValueSource( new ObjectBasedValueSource( aProject ) );
535
536 interpolator.addValueSource( new MapBasedValueSource( aProject.getProperties() ) );
537
538 try
539 {
540
541 interpolatedSiteDescriptorContent = interpolator.interpolate( interpolatedSiteDescriptorContent,
542 "project" );
543 }
544 catch ( InterpolationException e )
545 {
546 throw new SiteToolException( "Cannot interpolate site descriptor: " + e.getMessage(), e );
547 }
548
549
550 props.put( "parentProject", "<menu ref=\"parent\"/>" );
551
552
553 props.put( "reports", "<menu ref=\"reports\"/>" );
554
555 return StringUtils.interpolate( interpolatedSiteDescriptorContent, props );
556 }
557
558
559 public MavenProject getParentProject( MavenProject aProject, List<MavenProject> reactorProjects,
560 ArtifactRepository localRepository )
561 {
562 checkNotNull( "aProject", aProject );
563 checkNotNull( "reactorProjects", reactorProjects );
564 checkNotNull( "localRepository", localRepository );
565
566 MavenProject parentProject = null;
567
568 MavenProject origParent = aProject.getParent();
569 if ( origParent != null )
570 {
571 for ( MavenProject reactorProject : reactorProjects )
572 {
573 if ( reactorProject.getGroupId().equals( origParent.getGroupId() )
574 && reactorProject.getArtifactId().equals( origParent.getArtifactId() )
575 && reactorProject.getVersion().equals( origParent.getVersion() ) )
576 {
577 parentProject = reactorProject;
578 break;
579 }
580 }
581
582 if ( parentProject == null && aProject.getBasedir() != null
583 && StringUtils.isNotEmpty( aProject.getModel().getParent().getRelativePath() ) )
584 {
585 try
586 {
587 File pomFile = new File( aProject.getBasedir(), aProject.getModel().getParent().getRelativePath() );
588
589 if ( pomFile.isDirectory() )
590 {
591 pomFile = new File( pomFile, "pom.xml" );
592 }
593 pomFile = new File( getNormalizedPath( pomFile.getPath() ) );
594
595 if ( pomFile.isFile() )
596 {
597 MavenProject mavenProject = mavenProjectBuilder.build( pomFile, localRepository, null );
598
599 if ( mavenProject.getGroupId().equals( origParent.getGroupId() )
600 && mavenProject.getArtifactId().equals( origParent.getArtifactId() )
601 && mavenProject.getVersion().equals( origParent.getVersion() ) )
602 {
603 parentProject = mavenProject;
604 }
605 }
606 }
607 catch ( ProjectBuildingException e )
608 {
609 getLogger().info( "Unable to load parent project from a relative path: " + e.getMessage() );
610 }
611 }
612
613 if ( parentProject == null )
614 {
615 try
616 {
617 parentProject = mavenProjectBuilder.buildFromRepository( aProject.getParentArtifact(), aProject
618 .getRemoteArtifactRepositories(), localRepository );
619 getLogger().info( "Parent project loaded from repository: " + parentProject.getId() );
620 }
621 catch ( ProjectBuildingException e )
622 {
623 getLogger().warn( "Unable to load parent project from repository: " + e.getMessage() );
624 }
625 }
626
627 if ( parentProject == null )
628 {
629
630
631 parentProject = origParent;
632 }
633 }
634 return parentProject;
635 }
636
637
638 public void populateParentMenu( DecorationModel decorationModel, Locale locale, MavenProject project,
639 MavenProject parentProject, boolean keepInheritedRefs )
640 {
641 checkNotNull( "decorationModel", decorationModel );
642 checkNotNull( "project", project );
643 checkNotNull( "parentProject", parentProject );
644
645 Menu menu = decorationModel.getMenuRef( "parent" );
646
647 if ( menu == null )
648 {
649 return;
650 }
651
652 if ( keepInheritedRefs && menu.isInheritAsRef() )
653 {
654 return;
655 }
656
657 final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale;
658
659 String parentUrl = getDistMgmntSiteUrl( parentProject );
660
661 if ( parentUrl != null )
662 {
663 if ( parentUrl.endsWith( "/" ) )
664 {
665 parentUrl += "index.html";
666 }
667 else
668 {
669 parentUrl += "/index.html";
670 }
671
672 parentUrl = getRelativePath( parentUrl, getDistMgmntSiteUrl( project ) );
673 }
674 else
675 {
676
677 File parentBasedir = parentProject.getBasedir();
678
679 if ( parentBasedir != null )
680 {
681
682 String parentPath = parentBasedir.getAbsolutePath();
683 String projectPath = project.getBasedir().getAbsolutePath();
684 parentUrl = getRelativePath( parentPath, projectPath ) + "/index.html";
685 }
686 }
687
688
689 if ( parentUrl == null )
690 {
691 getLogger().warn( "Unable to find a URL to the parent project. The parent menu will NOT be added." );
692 }
693 else
694 {
695 if ( menu.getName() == null )
696 {
697 menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.parentproject" ) );
698 }
699
700 MenuItem item = new MenuItem();
701 item.setName( parentProject.getName() );
702 item.setHref( parentUrl );
703 menu.addItem( item );
704 }
705 }
706
707
708
709
710
711
712 public void populateProjectParentMenu( DecorationModel decorationModel, Locale locale, MavenProject project,
713 MavenProject parentProject, boolean keepInheritedRefs )
714 {
715 populateParentMenu( decorationModel, locale, project, parentProject, keepInheritedRefs );
716 }
717
718
719
720
721
722
723
724 public void populateModules( MavenProject project, List<MavenProject> reactorProjects,
725 ArtifactRepository localRepository, DecorationModel decorationModel, Locale locale,
726 boolean keepInheritedRefs )
727 throws SiteToolException
728 {
729 populateModulesMenu( project, reactorProjects, localRepository, decorationModel, locale, keepInheritedRefs );
730 }
731
732
733 public void populateModulesMenu( MavenProject project, List<MavenProject> reactorProjects,
734 ArtifactRepository localRepository, DecorationModel decorationModel,
735 Locale locale, boolean keepInheritedRefs )
736 throws SiteToolException
737 {
738 checkNotNull( "project", project );
739 checkNotNull( "reactorProjects", reactorProjects );
740 checkNotNull( "localRepository", localRepository );
741 checkNotNull( "decorationModel", decorationModel );
742
743 Menu menu = decorationModel.getMenuRef( "modules" );
744
745 if ( menu == null )
746 {
747 return;
748 }
749
750 if ( keepInheritedRefs && menu.isInheritAsRef() )
751 {
752 return;
753 }
754
755 final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale ;
756
757
758 if ( project.getModules().size() > 0 )
759 {
760 List<MavenProject> projects = reactorProjects;
761
762 if ( menu.getName() == null )
763 {
764 menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectmodules" ) );
765 }
766
767 getLogger().debug( "Attempting to load module information from local filesystem" );
768
769
770 List<Model> models = new ArrayList<Model>( project.getModules().size() );
771 for ( Iterator<String> i = project.getModules().iterator(); i.hasNext(); )
772 {
773 String module = i.next();
774 Model model;
775 File f = new File( project.getBasedir(), module + "/pom.xml" );
776 if ( f.exists() )
777 {
778 try
779 {
780 model = mavenProjectBuilder.build( f, localRepository, null ).getModel();
781 }
782 catch ( ProjectBuildingException e )
783 {
784 throw new SiteToolException( "Unable to read local module-POM", e );
785 }
786 }
787 else
788 {
789 getLogger().warn( "No filesystem module-POM available" );
790
791 model = new Model();
792 model.setName( module );
793 setDistMgmntSiteUrl( model, module );
794 }
795 models.add( model );
796 }
797 populateModulesMenuItemsFromModels( project, models, menu );
798 }
799 else if ( decorationModel.getMenuRef( "modules" ).getInherit() == null )
800 {
801
802 decorationModel.removeMenuRef( "modules" );
803 }
804 }
805
806
807 public List<Locale> getAvailableLocales( String locales )
808 {
809 if ( locales == null )
810 {
811 return Collections.singletonList( DEFAULT_LOCALE );
812 }
813
814 String[] localesArray = StringUtils.split( locales, "," );
815 List<Locale> localesList = new ArrayList<Locale>( localesArray.length );
816
817 for ( int i = 0; i < localesArray.length; i++ )
818 {
819 Locale locale = codeToLocale( localesArray[i] );
820
821 if ( locale == null )
822 {
823 continue;
824 }
825
826 if ( !Arrays.asList( Locale.getAvailableLocales() ).contains( locale ) )
827 {
828 if ( getLogger().isWarnEnabled() )
829 {
830 getLogger().warn( "The locale parsed defined by '" + locale
831 + "' is not available in this Java Virtual Machine ("
832 + System.getProperty( "java.version" )
833 + " from " + System.getProperty( "java.vendor" ) + ") - IGNORING" );
834 }
835 continue;
836 }
837
838
839 if ( ( !locale.getLanguage().equals( DEFAULT_LOCALE.getLanguage() ) )
840 && ( !i18n.getBundle( "site-tool", locale ).getLocale().getLanguage()
841 .equals( locale.getLanguage() ) ) )
842 {
843 if ( getLogger().isWarnEnabled() )
844 {
845 getLogger().warn( "The locale '" + locale + "' (" + locale.getDisplayName( Locale.ENGLISH )
846 + ") is not currently support by Maven - IGNORING."
847 + "\nContribution are welcome and greatly appreciated!"
848 + "\nIf you want to contribute a new translation, please visit "
849 + "http://maven.apache.org/plugins/maven-site-plugin/i18n.html for detailed instructions." );
850 }
851
852 continue;
853 }
854
855 localesList.add( locale );
856 }
857
858 if ( localesList.isEmpty() )
859 {
860 localesList = Collections.singletonList( DEFAULT_LOCALE );
861 }
862
863 return localesList;
864 }
865
866
867 public Locale codeToLocale( String localeCode )
868 {
869 if ( localeCode == null )
870 {
871 return null;
872 }
873
874 if ( "default".equalsIgnoreCase( localeCode ) )
875 {
876 return Locale.getDefault();
877 }
878
879 String language = "";
880 String country = "";
881 String variant = "";
882
883 StringTokenizer tokenizer = new StringTokenizer( localeCode, "_" );
884 final int maxTokens = 3;
885 if ( tokenizer.countTokens() > maxTokens )
886 {
887 if ( getLogger().isWarnEnabled() )
888 {
889 getLogger().warn( "Invalid java.util.Locale format for '" + localeCode + "' entry - IGNORING" );
890 }
891 return null;
892 }
893
894 if ( tokenizer.hasMoreTokens() )
895 {
896 language = tokenizer.nextToken();
897 if ( tokenizer.hasMoreTokens() )
898 {
899 country = tokenizer.nextToken();
900 if ( tokenizer.hasMoreTokens() )
901 {
902 variant = tokenizer.nextToken();
903 }
904 }
905 }
906
907 return new Locale( language, country, variant );
908 }
909
910
911
912
913
914
915
916
917
918
919 protected static String getNormalizedPath( String path )
920 {
921 String normalized = FilenameUtils.normalize( path );
922 if ( normalized == null )
923 {
924 normalized = path;
925 }
926 return ( normalized == null ) ? null : normalized.replace( '\\', '/' );
927 }
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943 private File resolveSiteDescriptor( MavenProject project, ArtifactRepository localRepository,
944 List<ArtifactRepository> repositories, Locale locale )
945 throws IOException, ArtifactResolutionException, ArtifactNotFoundException
946 {
947 File result;
948
949
950 Artifact artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(),
951 project.getArtifactId(),
952 project.getVersion(), "xml",
953 "site_" + locale.getLanguage() );
954
955 boolean found = false;
956 try
957 {
958 artifactResolver.resolve( artifact, repositories, localRepository );
959
960 result = artifact.getFile();
961
962
963 if ( result.length() > 0 )
964 {
965 found = true;
966 }
967 else
968 {
969 getLogger().debug( "Skipped site descriptor for locale " + locale.getLanguage() );
970 }
971 }
972 catch ( ArtifactNotFoundException e )
973 {
974 getLogger().debug( "Unable to locate site descriptor for locale " + locale.getLanguage() + ": " + e );
975
976
977
978 result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) );
979 result.getParentFile().mkdirs();
980 result.createNewFile();
981 }
982
983 if ( !found )
984 {
985 artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), project.getArtifactId(),
986 project.getVersion(), "xml", "site" );
987 try
988 {
989 artifactResolver.resolve( artifact, repositories, localRepository );
990 }
991 catch ( ArtifactNotFoundException e )
992 {
993
994 result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) );
995 result.getParentFile().mkdirs();
996 result.createNewFile();
997
998 throw e;
999 }
1000
1001 result = artifact.getFile();
1002
1003
1004 if ( result.length() == 0 )
1005 {
1006 getLogger().debug( "Skipped remote site descriptor check" );
1007 result = null;
1008 }
1009 }
1010
1011 return result;
1012 }
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025 private DecorationModel getDecorationModel( MavenProject project, MavenProject parentProject,
1026 List<MavenProject> reactorProjects, ArtifactRepository localRepository,
1027 List<ArtifactRepository> repositories, String siteDirectory,
1028 Locale locale, Map<String, String> origProps )
1029 throws SiteToolException
1030 {
1031 Map<String, String> props = new HashMap<String, String>( origProps );
1032
1033 File siteDescriptor;
1034 if ( project.getBasedir() == null )
1035 {
1036
1037 try
1038 {
1039 siteDescriptor = getSiteDescriptorFromRepository( project, localRepository, repositories, locale );
1040 }
1041 catch ( SiteToolException e )
1042 {
1043 throw new SiteToolException( "The site descriptor cannot be resolved from the repository: "
1044 + e.getMessage(), e );
1045 }
1046 }
1047 else
1048 {
1049 siteDescriptor = getSiteDescriptorFromBasedir( siteDirectory, project.getBasedir(), locale );
1050 }
1051
1052 String siteDescriptorContent = null;
1053 long siteDescriptorLastModified = 0L;
1054 Reader siteDescriptorReader = null;
1055 try
1056 {
1057 if ( siteDescriptor != null && siteDescriptor.exists() )
1058 {
1059 getLogger().debug( "Reading site descriptor from " + siteDescriptor );
1060 siteDescriptorReader = ReaderFactory.newXmlReader( siteDescriptor );
1061 siteDescriptorContent = IOUtil.toString( siteDescriptorReader );
1062 siteDescriptorLastModified = siteDescriptor.lastModified();
1063 }
1064 }
1065 catch ( IOException e )
1066 {
1067 throw new SiteToolException( "The site descriptor cannot be read!", e );
1068 }
1069 finally
1070 {
1071 IOUtil.close( siteDescriptorReader );
1072 }
1073
1074 DecorationModel decoration = null;
1075 if ( siteDescriptorContent != null )
1076 {
1077 siteDescriptorContent = getInterpolatedSiteDescriptorContent( props, project, siteDescriptorContent );
1078
1079 decoration = readDecorationModel( siteDescriptorContent );
1080 decoration.setLastModified( siteDescriptorLastModified );
1081 }
1082
1083 if ( parentProject != null && ( decoration == null || decoration.isMergeParent() ) )
1084 {
1085 getLogger().debug( "Loading parent project site descriptor..." );
1086
1087 MavenProject parentParentProject = getParentProject( parentProject, reactorProjects, localRepository );
1088
1089 DecorationModel parent =
1090 getDecorationModel( parentProject, parentParentProject, reactorProjects, localRepository, repositories,
1091 siteDirectory, locale, props );
1092
1093
1094
1095 if ( decoration == null && parent != null )
1096 {
1097
1098 decoration = new DecorationModel();
1099 }
1100
1101 String name = project.getName();
1102 if ( decoration != null && StringUtils.isNotEmpty( decoration.getName() ) )
1103 {
1104 name = decoration.getName();
1105 }
1106
1107
1108 String projectDistMgmnt = getDistMgmntSiteUrl( project );
1109 String parentDistMgmnt = getDistMgmntSiteUrl( parentProject );
1110 assembler.assembleModelInheritance( name, decoration, parent, projectDistMgmnt,
1111 parentDistMgmnt == null ? projectDistMgmnt : parentDistMgmnt );
1112 }
1113
1114 if ( decoration != null && decoration.getSkin() != null )
1115 {
1116 getLogger().debug( "Skin used: " + decoration.getSkin() );
1117 }
1118
1119 return decoration;
1120 }
1121
1122
1123
1124
1125
1126
1127 private DecorationModel readDecorationModel( String siteDescriptorContent )
1128 throws SiteToolException
1129 {
1130 DecorationModel decoration;
1131 try
1132 {
1133 decoration = new DecorationXpp3Reader().read( new StringReader( siteDescriptorContent ) );
1134 }
1135 catch ( XmlPullParserException e )
1136 {
1137 throw new SiteToolException( "Error parsing site descriptor", e );
1138 }
1139 catch ( IOException e )
1140 {
1141 throw new SiteToolException( "Error reading site descriptor", e );
1142 }
1143 return decoration;
1144 }
1145
1146 private static String buildRelativePath( final String toPath, final String fromPath, final char separatorChar )
1147 {
1148
1149 StringTokenizer toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) );
1150 StringTokenizer fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) );
1151
1152 int count = 0;
1153
1154
1155 while ( toTokeniser.hasMoreTokens() && fromTokeniser.hasMoreTokens() )
1156 {
1157 if ( separatorChar == '\\' )
1158 {
1159 if ( !fromTokeniser.nextToken().equalsIgnoreCase( toTokeniser.nextToken() ) )
1160 {
1161 break;
1162 }
1163 }
1164 else
1165 {
1166 if ( !fromTokeniser.nextToken().equals( toTokeniser.nextToken() ) )
1167 {
1168 break;
1169 }
1170 }
1171
1172 count++;
1173 }
1174
1175
1176
1177
1178 toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) );
1179 fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) );
1180
1181 while ( count-- > 0 )
1182 {
1183 fromTokeniser.nextToken();
1184 toTokeniser.nextToken();
1185 }
1186
1187 StringBuilder relativePath = new StringBuilder();
1188
1189
1190 while ( fromTokeniser.hasMoreTokens() )
1191 {
1192 fromTokeniser.nextToken();
1193
1194 relativePath.append( ".." );
1195
1196 if ( fromTokeniser.hasMoreTokens() )
1197 {
1198 relativePath.append( separatorChar );
1199 }
1200 }
1201
1202 if ( relativePath.length() != 0 && toTokeniser.hasMoreTokens() )
1203 {
1204 relativePath.append( separatorChar );
1205 }
1206
1207
1208 while ( toTokeniser.hasMoreTokens() )
1209 {
1210 relativePath.append( toTokeniser.nextToken() );
1211
1212 if ( toTokeniser.hasMoreTokens() )
1213 {
1214 relativePath.append( separatorChar );
1215 }
1216 }
1217 return relativePath.toString();
1218 }
1219
1220
1221
1222
1223
1224
1225 private void populateModulesMenuItemsFromModels( MavenProject project, List<Model> models, Menu menu )
1226 {
1227 for ( Model model : models )
1228 {
1229 String reactorUrl = getDistMgmntSiteUrl( model );
1230 String name = name( model );
1231
1232 appendMenuItem( project, menu, name, reactorUrl, model.getArtifactId() );
1233 }
1234 }
1235
1236 private static String name( final Model model )
1237 {
1238 String name = model.getName();
1239
1240 if ( name == null )
1241 {
1242 name = "Unnamed - " + model.getGroupId() + ":" + model.getArtifactId() + ":"
1243 + model.getPackaging() + ":" + model.getVersion();
1244 }
1245
1246 return name;
1247 }
1248
1249
1250
1251
1252
1253
1254
1255
1256 private void appendMenuItem( MavenProject project, Menu menu, String name, String href, String defaultHref )
1257 {
1258 String selectedHref = href;
1259
1260 if ( selectedHref == null )
1261 {
1262 selectedHref = defaultHref;
1263 }
1264
1265 MenuItem item = new MenuItem();
1266 item.setName( name );
1267
1268 String baseUrl = getDistMgmntSiteUrl( project );
1269 if ( baseUrl != null )
1270 {
1271 selectedHref = getRelativePath( selectedHref, baseUrl );
1272 }
1273
1274 if ( selectedHref.endsWith( "/" ) )
1275 {
1276 item.setHref( selectedHref + "index.html" );
1277 }
1278 else
1279 {
1280 item.setHref( selectedHref + "/index.html" );
1281 }
1282 menu.addItem( item );
1283 }
1284
1285
1286
1287
1288
1289
1290
1291
1292 private MenuItem createCategoryMenu( String name, String href, List<MavenReport> categoryReports, Locale locale )
1293 {
1294 MenuItem item = new MenuItem();
1295 item.setName( name );
1296 item.setCollapse( true );
1297 item.setHref( href );
1298
1299
1300
1301
1302 for ( MavenReport report : categoryReports )
1303 {
1304 MenuItem subitem = new MenuItem();
1305 subitem.setName( report.getName( locale ) );
1306 subitem.setHref( report.getOutputName() + ".html" );
1307 item.getItems().add( subitem );
1308 }
1309
1310 return item;
1311 }
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323 private static boolean isEmptyList( List<?> list )
1324 {
1325 return list == null || list.isEmpty();
1326 }
1327
1328
1329
1330
1331
1332
1333
1334 private static String getDistMgmntSiteUrl( MavenProject project )
1335 {
1336 return getDistMgmntSiteUrl( project.getDistributionManagement() );
1337 }
1338
1339
1340
1341
1342
1343
1344
1345 private static String getDistMgmntSiteUrl( Model model )
1346 {
1347 return getDistMgmntSiteUrl( model.getDistributionManagement() );
1348 }
1349
1350 private static String getDistMgmntSiteUrl( DistributionManagement distMgmnt )
1351 {
1352 if ( distMgmnt != null && distMgmnt.getSite() != null && distMgmnt.getSite().getUrl() != null )
1353 {
1354 return urlEncode( distMgmnt.getSite().getUrl() );
1355 }
1356
1357 return null;
1358 }
1359
1360 private static String urlEncode( final String url )
1361 {
1362 if ( url == null )
1363 {
1364 return null;
1365 }
1366
1367 try
1368 {
1369 return new File( url ).toURI().toURL().toExternalForm();
1370 }
1371 catch ( MalformedURLException ex )
1372 {
1373 return url;
1374 }
1375 }
1376
1377 private static void setDistMgmntSiteUrl( Model model, String url )
1378 {
1379 if ( model.getDistributionManagement() == null )
1380 {
1381 model.setDistributionManagement( new DistributionManagement() );
1382 }
1383
1384 if ( model.getDistributionManagement().getSite() == null )
1385 {
1386 model.getDistributionManagement().setSite( new Site() );
1387 }
1388
1389 model.getDistributionManagement().getSite().setUrl( url );
1390 }
1391
1392 private void checkNotNull( String name, Object value )
1393 {
1394 if ( value == null )
1395 {
1396 throw new IllegalArgumentException( "The parameter '" + name + "' cannot be null." );
1397 }
1398 }
1399 }