| package org.apache.maven.plugins.site.render; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import org.apache.maven.artifact.Artifact; |
| import org.apache.maven.doxia.site.decoration.DecorationModel; |
| import org.apache.maven.doxia.site.decoration.Menu; |
| import org.apache.maven.doxia.site.decoration.MenuItem; |
| import org.apache.maven.doxia.siterenderer.DocumentRenderer; |
| import org.apache.maven.doxia.siterenderer.Renderer; |
| import org.apache.maven.doxia.siterenderer.RendererException; |
| import org.apache.maven.doxia.siterenderer.RenderingContext; |
| import org.apache.maven.doxia.siterenderer.SiteRenderingContext; |
| import org.apache.maven.doxia.tools.SiteToolException; |
| import org.apache.maven.execution.MavenSession; |
| import org.apache.maven.model.ReportPlugin; |
| import org.apache.maven.model.Reporting; |
| import org.apache.maven.plugin.MojoExecutionException; |
| import org.apache.maven.plugin.MojoFailureException; |
| import org.apache.maven.plugin.descriptor.PluginDescriptor; |
| import org.apache.maven.plugins.annotations.Component; |
| import org.apache.maven.plugins.annotations.Parameter; |
| import org.apache.maven.plugins.site.descriptor.AbstractSiteDescriptorMojo; |
| import org.apache.maven.reporting.MavenReport; |
| import org.apache.maven.reporting.exec.MavenReportExecution; |
| import org.apache.maven.reporting.exec.MavenReportExecutor; |
| import org.apache.maven.reporting.exec.MavenReportExecutorRequest; |
| import org.codehaus.plexus.util.ReaderFactory; |
| import org.codehaus.plexus.util.StringUtils; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| |
| import static org.apache.maven.shared.utils.logging.MessageUtils.buffer; |
| |
| /** |
| * Base class for site rendering mojos. |
| * |
| * @author <a href="mailto:brett@apache.org">Brett Porter</a> |
| * |
| */ |
| public abstract class AbstractSiteRenderingMojo extends AbstractSiteDescriptorMojo |
| { |
| /** |
| * Module type exclusion mappings |
| * ex: <code>fml -> **/*-m1.fml</code> (excludes fml files ending in '-m1.fml' recursively) |
| * <p/> |
| * The configuration looks like this: |
| * <pre> |
| * <moduleExcludes> |
| * <moduleType>filename1.ext,**/*sample.ext</moduleType> |
| * <!-- moduleType can be one of 'apt', 'fml' or 'xdoc'. --> |
| * <!-- The value is a comma separated list of --> |
| * <!-- filenames or fileset patterns. --> |
| * <!-- Here's an example: --> |
| * <xdoc>changes.xml,navigation.xml</xdoc> |
| * </moduleExcludes> |
| * </pre> |
| */ |
| @Parameter |
| private Map<String, String> moduleExcludes; |
| |
| /** |
| * The location of a Velocity template file to use. When used, skins and the default templates, CSS and images |
| * are disabled. It is highly recommended that you package this as a skin instead. |
| * |
| * @since 2.0-beta-5 |
| */ |
| @Parameter( property = "templateFile" ) |
| private File templateFile; |
| |
| /** |
| * Additional template properties for rendering the site. See |
| * <a href="/doxia/doxia-sitetools/doxia-site-renderer/">Doxia Site Renderer</a>. |
| */ |
| @Parameter |
| private Map<String, Object> attributes; |
| |
| /** |
| * Site renderer. |
| */ |
| @Component |
| protected Renderer siteRenderer; |
| |
| /** |
| * Reports (Maven 2). |
| */ |
| @Parameter( defaultValue = "${reports}", required = true, readonly = true ) |
| protected List<MavenReport> reports; |
| |
| /** |
| * Alternative directory for xdoc source, useful for m1 to m2 migration |
| * |
| * @deprecated use the standard m2 directory layout |
| */ |
| @Parameter( defaultValue = "${basedir}/xdocs" ) |
| private File xdocDirectory; |
| |
| /** |
| * Directory containing generated documentation in source format (Doxia supported markup). |
| * This is used to pick up other source docs that might have been generated at build time (by reports or any other |
| * build time mean). |
| * This directory is expected to have the same structure as <code>siteDirectory</code> |
| * (ie. one directory per Doxia-source-supported markup types). |
| * |
| * todo should we deprecate in favour of reports directly using Doxia Sink API, without this Doxia source |
| * intermediate step? |
| */ |
| @Parameter( alias = "workingDirectory", defaultValue = "${project.build.directory}/generated-site" ) |
| protected File generatedSiteDirectory; |
| |
| /** |
| * The current Maven session. |
| */ |
| @Parameter( defaultValue = "${session}", readonly = true, required = true ) |
| protected MavenSession mavenSession; |
| |
| /** |
| * replaces previous reportPlugins parameter, that was injected by Maven core from |
| * reporting section: but this new configuration format has been abandoned. |
| * |
| * @since 3.7.1 |
| */ |
| @Parameter( defaultValue = "${project.reporting}", readonly = true ) |
| private Reporting reporting; |
| |
| /** |
| * Whether to generate the summary page for project reports: project-info.html. |
| * |
| * @since 2.3 |
| */ |
| @Parameter( property = "generateProjectInfo", defaultValue = "true" ) |
| private boolean generateProjectInfo; |
| |
| /** |
| * Specifies the input encoding. |
| * |
| * @since 2.3 |
| */ |
| @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" ) |
| private String inputEncoding; |
| |
| /** |
| * Specifies the output encoding. |
| * |
| * @since 2.3 |
| */ |
| @Parameter( property = "outputEncoding", defaultValue = "${project.reporting.outputEncoding}" ) |
| private String outputEncoding; |
| |
| @Component |
| protected MavenReportExecutor mavenReportExecutor; |
| |
| /** |
| * Gets the input files encoding. |
| * |
| * @return The input files encoding, never <code>null</code>. |
| */ |
| protected String getInputEncoding() |
| { |
| return ( StringUtils.isEmpty( inputEncoding ) ) ? ReaderFactory.FILE_ENCODING : inputEncoding; |
| } |
| |
| /** |
| * Gets the effective reporting output files encoding. |
| * |
| * @return The effective reporting output file encoding, never <code>null</code>. |
| */ |
| protected String getOutputEncoding() |
| { |
| return ( outputEncoding == null ) ? ReaderFactory.UTF_8 : outputEncoding; |
| } |
| |
| /** |
| * Whether to save Velocity processed Doxia content (<code>*.<ext>.vm</code>) |
| * to <code>${generatedSiteDirectory}/processed</code>. |
| * |
| * @since 3.5 |
| */ |
| @Parameter |
| private boolean saveProcessedContent; |
| |
| protected void checkInputEncoding() |
| { |
| if ( StringUtils.isEmpty( inputEncoding ) ) |
| { |
| getLog().warn( "Input file encoding has not been set, using platform encoding " |
| + ReaderFactory.FILE_ENCODING + ", i.e. build is platform dependent!" ); |
| } |
| } |
| |
| protected List<MavenReportExecution> getReports() |
| throws MojoExecutionException |
| { |
| MavenReportExecutorRequest mavenReportExecutorRequest = new MavenReportExecutorRequest(); |
| mavenReportExecutorRequest.setLocalRepository( localRepository ); |
| mavenReportExecutorRequest.setMavenSession( mavenSession ); |
| mavenReportExecutorRequest.setProject( project ); |
| mavenReportExecutorRequest.setReportPlugins( getReportingPlugins() ); |
| |
| List<MavenReportExecution> allReports = mavenReportExecutor.buildMavenReports( mavenReportExecutorRequest ); |
| |
| // filter out reports that can't be generated |
| List<MavenReportExecution> reportExecutions = new ArrayList<>( allReports.size() ); |
| for ( MavenReportExecution exec : allReports ) |
| { |
| if ( exec.canGenerateReport() ) |
| { |
| reportExecutions.add( exec ); |
| } |
| } |
| return reportExecutions; |
| } |
| |
| /** |
| * Get the report plugins from reporting section, adding if necessary (i.e. not excluded) |
| * default reports (i.e. maven-project-info-reports) |
| * |
| * @return the effective list of reports |
| * @since 3.7.1 |
| */ |
| private ReportPlugin[] getReportingPlugins() |
| { |
| List<ReportPlugin> reportingPlugins = reporting.getPlugins(); |
| |
| // MSITE-806: add default report plugin like done in maven-model-builder DefaultReportingConverter |
| boolean hasMavenProjectInfoReportsPlugin = false; |
| for ( ReportPlugin plugin : reportingPlugins ) |
| { |
| if ( "org.apache.maven.plugins".equals( plugin.getGroupId() ) |
| && "maven-project-info-reports-plugin".equals( plugin.getArtifactId() ) ) |
| { |
| hasMavenProjectInfoReportsPlugin = true; |
| break; |
| } |
| } |
| |
| if ( !reporting.isExcludeDefaults() && !hasMavenProjectInfoReportsPlugin ) |
| { |
| ReportPlugin mpir = new ReportPlugin(); |
| mpir.setArtifactId( "maven-project-info-reports-plugin" ); |
| reportingPlugins.add( mpir ); |
| } |
| return reportingPlugins.toArray( new ReportPlugin[reportingPlugins.size()] ); |
| } |
| |
| protected SiteRenderingContext createSiteRenderingContext( Locale locale ) |
| throws MojoExecutionException, IOException, MojoFailureException |
| { |
| DecorationModel decorationModel = prepareDecorationModel( locale ); |
| if ( attributes == null ) |
| { |
| attributes = new HashMap<>(); |
| } |
| |
| if ( attributes.get( "project" ) == null ) |
| { |
| attributes.put( "project", project ); |
| } |
| |
| if ( attributes.get( "inputEncoding" ) == null ) |
| { |
| attributes.put( "inputEncoding", getInputEncoding() ); |
| } |
| |
| if ( attributes.get( "outputEncoding" ) == null ) |
| { |
| attributes.put( "outputEncoding", getOutputEncoding() ); |
| } |
| |
| // Put any of the properties in directly into the Velocity context |
| for ( Map.Entry<Object, Object> entry : project.getProperties().entrySet() ) |
| { |
| attributes.put( (String) entry.getKey(), entry.getValue() ); |
| } |
| |
| SiteRenderingContext context; |
| if ( templateFile != null ) |
| { |
| getLog().info( buffer().a( "Rendering content with " ).strong( templateFile |
| + " template file" ).a( '.' ).toString() ); |
| |
| if ( !templateFile.exists() ) |
| { |
| throw new MojoFailureException( "Template file '" + templateFile + "' does not exist" ); |
| } |
| context = siteRenderer.createContextForTemplate( templateFile, attributes, decorationModel, |
| project.getName(), locale ); |
| } |
| else |
| { |
| try |
| { |
| Artifact skinArtifact = |
| siteTool.getSkinArtifactFromRepository( localRepository, repositories, decorationModel ); |
| |
| getLog().info( buffer().a( "Rendering content with " ).strong( skinArtifact.getId() |
| + " skin" ).a( '.' ).toString() ); |
| |
| context = siteRenderer.createContextForSkin( skinArtifact, attributes, decorationModel, |
| project.getName(), locale ); |
| } |
| catch ( SiteToolException e ) |
| { |
| throw new MojoExecutionException( "SiteToolException while preparing skin: " + e.getMessage(), e ); |
| } |
| catch ( RendererException e ) |
| { |
| throw new MojoExecutionException( "RendererException while preparing context for skin: " |
| + e.getMessage(), e ); |
| } |
| } |
| |
| // Generate static site |
| context.setRootDirectory( project.getBasedir() ); |
| if ( !locale.getLanguage().equals( Locale.getDefault().getLanguage() ) ) |
| { |
| context.addSiteDirectory( new File( siteDirectory, locale.getLanguage() ) ); |
| context.addModuleDirectory( new File( xdocDirectory, locale.getLanguage() ), "xdoc" ); |
| context.addModuleDirectory( new File( xdocDirectory, locale.getLanguage() ), "fml" ); |
| } |
| else |
| { |
| context.addSiteDirectory( siteDirectory ); |
| context.addModuleDirectory( xdocDirectory, "xdoc" ); |
| context.addModuleDirectory( xdocDirectory, "fml" ); |
| } |
| |
| if ( moduleExcludes != null ) |
| { |
| context.setModuleExcludes( moduleExcludes ); |
| } |
| |
| if ( saveProcessedContent ) |
| { |
| context.setProcessedContentOutput( new File( generatedSiteDirectory, "processed" ) ); |
| } |
| |
| return context; |
| } |
| |
| /** |
| * Go through the list of reports and process each one like this: |
| * <ul> |
| * <li>Add the report to a map of reports keyed by filename having the report itself as value |
| * <li>If the report is not yet in the map of documents, add it together with a suitable renderer |
| * </ul> |
| * |
| * @param reports A List of MavenReports |
| * @param documents A Map of documents, keyed by filename |
| * @param locale the Locale the reports are processed for. |
| * @return A map with all reports keyed by filename having the report itself as value. |
| * The map will be used to populate a menu. |
| */ |
| protected Map<String, MavenReport> locateReports( List<MavenReportExecution> reports, |
| Map<String, DocumentRenderer> documents, Locale locale ) |
| { |
| // copy Collection to prevent ConcurrentModificationException |
| List<MavenReportExecution> filtered = new ArrayList<>( reports ); |
| |
| Map<String, MavenReport> reportsByOutputName = new LinkedHashMap<>(); |
| for ( MavenReportExecution mavenReportExecution : filtered ) |
| { |
| MavenReport report = mavenReportExecution.getMavenReport(); |
| |
| String outputName = report.getOutputName() + ".html"; |
| |
| // Always add the report to the menu, see MSITE-150 |
| reportsByOutputName.put( report.getOutputName(), report ); |
| |
| if ( documents.containsKey( outputName ) ) |
| { |
| String reportMojoInfo = |
| ( mavenReportExecution.getGoal() == null ) ? "" : ( " (" |
| + mavenReportExecution.getPlugin().getArtifactId() + ':' |
| + mavenReportExecution.getPlugin().getVersion() + ':' + mavenReportExecution.getGoal() + ')' ); |
| |
| getLog().info( "Skipped \"" + report.getName( locale ) + "\" report" + reportMojoInfo + ", file \"" |
| + outputName + "\" already exists." ); |
| |
| reports.remove( mavenReportExecution ); |
| } |
| else |
| { |
| String reportMojoInfo = mavenReportExecution.getPlugin().getGroupId() + ':' |
| + mavenReportExecution.getPlugin().getArtifactId() + ':' |
| + mavenReportExecution.getPlugin().getVersion() + ':' + mavenReportExecution.getGoal(); |
| RenderingContext renderingContext = new RenderingContext( siteDirectory, outputName, reportMojoInfo ); |
| DocumentRenderer renderer = |
| new ReportDocumentRenderer( mavenReportExecution, renderingContext, getLog() ); |
| documents.put( outputName, renderer ); |
| } |
| } |
| return reportsByOutputName; |
| } |
| |
| /** |
| * Go through the collection of reports and put each report into a list for the appropriate category. The list is |
| * put into a map keyed by the name of the category. |
| * |
| * @param reports A Collection of MavenReports |
| * @return A map keyed category having the report itself as value |
| */ |
| protected Map<String, List<MavenReport>> categoriseReports( Collection<MavenReport> reports ) |
| { |
| Map<String, List<MavenReport>> categories = new LinkedHashMap<>(); |
| for ( MavenReport report : reports ) |
| { |
| List<MavenReport> categoryReports = categories.get( report.getCategoryName() ); |
| if ( categoryReports == null ) |
| { |
| categoryReports = new ArrayList<>(); |
| categories.put( report.getCategoryName(), categoryReports ); |
| } |
| categoryReports.add( report ); |
| } |
| return categories; |
| } |
| |
| /** |
| * Locate every document to be rendered for given locale:<ul> |
| * <li>handwritten content, ie Doxia files,</li> |
| * <li>reports,</li> |
| * <li>"Project Information" and "Project Reports" category summaries.</li> |
| * </ul> |
| * |
| * @param context the site context |
| * @param reports the documents |
| * @param locale the locale |
| * @return the documents and their renderers |
| * @throws IOException in case of file reading issue |
| * @throws RendererException in case of Doxia rendering issue |
| * @see CategorySummaryDocumentRenderer |
| */ |
| protected Map<String, DocumentRenderer> locateDocuments( SiteRenderingContext context, |
| List<MavenReportExecution> reports, Locale locale ) |
| throws IOException, RendererException |
| { |
| Map<String, DocumentRenderer> documents = siteRenderer.locateDocumentFiles( context, true ); |
| |
| Map<String, MavenReport> reportsByOutputName = locateReports( reports, documents, locale ); |
| |
| // TODO: I want to get rid of categories eventually. There's no way to add your own in a fully i18n manner |
| Map<String, List<MavenReport>> categories = categoriseReports( reportsByOutputName.values() ); |
| |
| siteTool.populateReportsMenu( context.getDecoration(), locale, categories ); |
| populateReportItems( context.getDecoration(), locale, reportsByOutputName ); |
| |
| if ( categories.containsKey( MavenReport.CATEGORY_PROJECT_INFORMATION ) && generateProjectInfo ) |
| { |
| // add "Project Information" category summary document |
| List<MavenReport> categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_INFORMATION ); |
| |
| RenderingContext renderingContext = |
| new RenderingContext( siteDirectory, "project-info.html", |
| getSitePluginInfo() + ":CategorySummaryDocumentRenderer" ); |
| String title = i18n.getString( "site-plugin", locale, "report.information.title" ); |
| String desc1 = i18n.getString( "site-plugin", locale, "report.information.description1" ); |
| String desc2 = i18n.getString( "site-plugin", locale, "report.information.description2" ); |
| DocumentRenderer renderer = new CategorySummaryDocumentRenderer( renderingContext, title, desc1, desc2, |
| i18n, categoryReports, getLog() ); |
| |
| if ( !documents.containsKey( renderer.getOutputName() ) ) |
| { |
| documents.put( renderer.getOutputName(), renderer ); |
| } |
| else |
| { |
| getLog().info( "Category summary '" + renderer.getOutputName() + "' skipped; already exists" ); |
| } |
| } |
| |
| if ( categories.containsKey( MavenReport.CATEGORY_PROJECT_REPORTS ) ) |
| { |
| // add "Project Reports" category summary document |
| List<MavenReport> categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_REPORTS ); |
| RenderingContext renderingContext = |
| new RenderingContext( siteDirectory, "project-reports.html", |
| getSitePluginInfo() + ":CategorySummaryDocumentRenderer" ); |
| String title = i18n.getString( "site-plugin", locale, "report.project.title" ); |
| String desc1 = i18n.getString( "site-plugin", locale, "report.project.description1" ); |
| String desc2 = i18n.getString( "site-plugin", locale, "report.project.description2" ); |
| DocumentRenderer renderer = new CategorySummaryDocumentRenderer( renderingContext, title, desc1, desc2, |
| i18n, categoryReports, getLog() ); |
| |
| if ( !documents.containsKey( renderer.getOutputName() ) ) |
| { |
| documents.put( renderer.getOutputName(), renderer ); |
| } |
| else |
| { |
| getLog().info( "Category summary '" + renderer.getOutputName() + "' skipped; already exists" ); |
| } |
| } |
| return documents; |
| } |
| |
| private String getSitePluginInfo() |
| { |
| PluginDescriptor pluginDescriptor = (PluginDescriptor) getPluginContext().get( "pluginDescriptor" ); |
| return pluginDescriptor.getId(); |
| } |
| protected void populateReportItems( DecorationModel decorationModel, Locale locale, |
| Map<String, MavenReport> reportsByOutputName ) |
| { |
| for ( Menu menu : decorationModel.getMenus() ) |
| { |
| populateItemRefs( menu.getItems(), locale, reportsByOutputName ); |
| } |
| } |
| |
| private void populateItemRefs( List<MenuItem> items, Locale locale, Map<String, MavenReport> reportsByOutputName ) |
| { |
| for ( Iterator<MenuItem> i = items.iterator(); i.hasNext(); ) |
| { |
| MenuItem item = i.next(); |
| |
| if ( item.getRef() != null ) |
| { |
| MavenReport report = reportsByOutputName.get( item.getRef() ); |
| |
| if ( report != null ) |
| { |
| if ( item.getName() == null ) |
| { |
| item.setName( report.getName( locale ) ); |
| } |
| |
| if ( item.getHref() == null || item.getHref().length() == 0 ) |
| { |
| item.setHref( report.getOutputName() + ".html" ); |
| } |
| } |
| else |
| { |
| getLog().warn( "Unrecognised reference: '" + item.getRef() + "'" ); |
| i.remove(); |
| } |
| } |
| |
| populateItemRefs( item.getItems(), locale, reportsByOutputName ); |
| } |
| } |
| } |