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