All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.maven.plugins.site.AbstractSiteRenderingMojo Maven / Gradle / Ivy

Go to download

The Maven Site Plugin is a plugin that generates a site for the current project.

The newest version!
package org.apache.maven.plugins.site;

/*
 * 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 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 org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.doxia.sink.render.RenderingContext;
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.site.decoration.inheritance.DecorationModelInheritanceAssembler;
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.SiteRenderingContext;
import org.apache.maven.doxia.tools.SiteToolException;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
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.apache.maven.reporting.exec.ReportPlugin;
import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.configuration.MergePolicy;
import org.codehaus.plexus.configuration.PlexusConfiguration;
import org.codehaus.plexus.configuration.PlexusConfigurationUtils;
import org.codehaus.plexus.configuration.PlexusConfigurationUtils.MergePolicyType;
import org.codehaus.plexus.context.Context;
import org.codehaus.plexus.context.ContextException;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;

/**
 * Base class for site rendering mojos.
 *
 * @author Brett Porter
 * @version $Id: AbstractSiteRenderingMojo.java 25140 2011-09-23 19:07:57Z edalquist $
 */
public abstract class AbstractSiteRenderingMojo
    extends AbstractSiteMojo implements Contextualizable
{
    /**
     * Module type exclusion mappings
     * ex: fml  -> **/*-m1.fml  (excludes fml files ending in '-m1.fml' recursively)
     * 

* The configuration looks like this: *

     *   <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>
     * 
* * @parameter */ private Map moduleExcludes; /** * The component for assembling inheritance. * * @component */ private DecorationModelInheritanceAssembler assembler; /** * Remote repositories used for the project. * * @todo this is used for site descriptor resolution - it should relate to the actual project but for some reason they are not always filled in * @parameter default-value="${project.remoteArtifactRepositories}" * @readonly */ private List repositories; /** * Directory containing the template page. * * @parameter expression="${templateDirectory}" default-value="src/site" * @deprecated use templateFile or skinning instead */ private File templateDirectory; /** * Default template page. * * @parameter expression="${template}" * @deprecated use templateFile or skinning instead */ private String template; /** * 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. * * @parameter expression="${templateFile}" * @since 2.0-beta-5 */ private File templateFile; /** * The template properties for rendering the site. * * @parameter */ private Map attributes; /** * Site renderer. * * @component */ protected Renderer siteRenderer; /** * Reports (Maven 2). * * @parameter expression="${reports}" * @required * @readonly */ protected List reports; /** * Alternative directory for xdoc source, useful for m1 to m2 migration * * @parameter default-value="${basedir}/xdocs" * @deprecated use the standard m2 directory layout */ private File xdocDirectory; /** * Directory containing generated documentation. * This is used to pick up other source docs that might have been generated at build time. * * @parameter alias="workingDirectory" default-value="${project.build.directory}/generated-site" * * @todo should we deprecate in favour of reports? */ protected File generatedSiteDirectory; /** * The current Maven session. * * @parameter expression="${session}" * @required * @readonly */ protected MavenSession mavenSession; /** * Report plugins (Maven 3). * * @parameter * @since 3.0-beta-1 */ private ReportPlugin[] reportPlugins; /** * The default merge policy for handling duplicate plugins * * @parameter expression="${defaultMergePolicy}" */ protected MergePolicy defaultMergePolicy = MergePolicy.BOTH; private PlexusContainer container; /** * Make links in the site descriptor relative to the project URL. * By default, any absolute links that appear in the site descriptor, * e.g. banner hrefs, breadcrumbs, menu links, etc., will be made relative to project.url. * * Links will not be changed if this is set to false, or if the project has no URL defined. * * @parameter expression="${relativizeDecorationLinks}" default-value="true" * * @since 2.3 */ private boolean relativizeDecorationLinks; /** * Whether to generate the summary page for project reports: project-info.html. * * @parameter expression="${generateProjectInfo}" default-value="true" * * @since 2.3 */ private boolean generateProjectInfo; /** {@inheritDoc} */ public void contextualize( Context context ) throws ContextException { container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY ); } private String configToString(PlexusConfiguration configuration) { if (configuration == null) { return "NO CONFIGURATION"; } final StringBuilder buf = new StringBuilder().append( '<' ).append( configuration.getName() ); for ( final String a : configuration.getAttributeNames() ) { buf.append( ' ' ).append( a ).append( "=\"" ).append( configuration.getAttribute( a ) ).append( '"' ); } if ( configuration.getChildCount() > 0 ) { buf.append( '>' ); for ( final PlexusConfiguration c : configuration.getChildren() ) { buf.append( c ); } buf.append( "' ); } else if ( null != configuration.getValue() ) { buf.append( '>' ).append( configuration.getValue() ).append( "' ); } else { buf.append( "/>" ); } return buf.append( '\n' ).toString(); } protected List getReports() throws MojoExecutionException { if ( isMaven3OrMore() ) { ReportPlugin[] filteredReportPlugins = filterReportPlugins(reportPlugins); MavenReportExecutorRequest mavenReportExecutorRequest = new MavenReportExecutorRequest(); mavenReportExecutorRequest.setLocalRepository( localRepository ); mavenReportExecutorRequest.setMavenSession( mavenSession ); mavenReportExecutorRequest.setProject( project ); mavenReportExecutorRequest.setReportPlugins( filteredReportPlugins ); MavenReportExecutor mavenReportExecutor; try { mavenReportExecutor = (MavenReportExecutor) container.lookup( MavenReportExecutor.class.getName() ); } catch ( ComponentLookupException e ) { throw new MojoExecutionException( "could not get MavenReportExecutor component", e ); } return mavenReportExecutor.buildMavenReports( mavenReportExecutorRequest ); } List reportExecutions = new ArrayList( reports.size() ); for ( MavenReport report : reports ) { if ( report.canGenerateReport() ) { reportExecutions.add( new MavenReportExecution( report ) ); } } return reportExecutions; } protected ReportPlugin[] filterReportPlugins(ReportPlugin[] reportPlugins) { final Log log = this.getLog(); //Map of report plugins (to handle merging), linked because order is important! Map reportPluginFilter = new LinkedHashMap(); for (int pluginIndex = 0; pluginIndex < reportPlugins.length; pluginIndex++) { ReportPlugin reportPlugin = reportPlugins[pluginIndex]; String pluginKey = reportPlugin.getGroupId() + ":" + reportPlugin.getArtifactId(); if (!reportPluginFilter.containsKey(pluginKey)) { //First time this plugin has been seen, just add it to the map reportPluginFilter.put(pluginKey, reportPlugin); } else { final PlexusConfiguration newConfiguration = reportPlugin.getConfiguration(); //Not the first time we've seen this plugin, figure out what to do final MergePolicy duplicateReportPolicy = MergePolicyType.SELF_MERGE_POLICY.getMergePolicy(newConfiguration, defaultMergePolicy); switch (duplicateReportPolicy) { case FIRST: { //Use first, nothing to do break; } case BOTH: { //TODO is there a better way? What happens if the same key exists later with a MERGE option? reportPluginFilter.put(pluginKey + pluginIndex, reportPlugin); } case LAST: { //Use last, replace the existing plugin reportPluginFilter.put(pluginKey, reportPlugin); break; } case MERGE: { //Merge the plugin configurations final ReportPlugin existingReportPlugin = reportPluginFilter.get(pluginKey); if (reportPlugin.getVersion() != null) { existingReportPlugin.setVersion(reportPlugin.getVersion()); } final PlexusConfiguration existingConfiguration = existingReportPlugin.getConfiguration(); final PlexusConfiguration mergedConfiguration = PlexusConfigurationUtils.merge(existingConfiguration, newConfiguration); existingReportPlugin.setConfiguration(mergedConfiguration); break; } default: { throw new IllegalArgumentException("Unsupported " + MergePolicy.class.getName() + ": " + duplicateReportPolicy); } } } } ReportPlugin[] filteredReportPlugins = reportPluginFilter.values().toArray(new ReportPlugin[reportPluginFilter.size()]); return filteredReportPlugins; } protected SiteRenderingContext createSiteRenderingContext( Locale locale ) throws MojoExecutionException, IOException, MojoFailureException { 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 entry : project.getProperties().entrySet() ) { attributes.put( (String) entry.getKey(), entry.getValue() ); } DecorationModel decorationModel; try { decorationModel = siteTool.getDecorationModel( project, reactorProjects, localRepository, repositories, siteTool.getRelativePath( siteDirectory.getAbsolutePath(), project.getBasedir().getAbsolutePath() ), locale, getInputEncoding(), getOutputEncoding() ); } catch ( SiteToolException e ) { throw new MojoExecutionException( "SiteToolException: " + e.getMessage(), e ); } if ( relativizeDecorationLinks ) { final String url = project.getUrl(); if ( url == null ) { getLog().warn( "No project URL defined - decoration links will not be relativized!" ); } else { getLog().info( "Relativizing decoration links with respect to project URL: " + url ); assembler.resolvePaths( decorationModel, url ); } } if ( template != null ) { if ( templateFile != null ) { getLog().warn( "'template' configuration is ignored when 'templateFile' is set" ); } else { templateFile = new File( templateDirectory, template ); } } File skinFile; try { Artifact skinArtifact = siteTool.getSkinArtifactFromRepository( localRepository, repositories, decorationModel ); getLog().info( "Rendering site with " + skinArtifact.getId() + " skin." ); skinFile = skinArtifact.getFile(); } catch ( SiteToolException e ) { throw new MojoExecutionException( "SiteToolException: " + e.getMessage(), e ); } SiteRenderingContext context; if ( templateFile != null ) { if ( !templateFile.exists() ) { throw new MojoFailureException( "Template file '" + templateFile + "' does not exist" ); } context = siteRenderer.createContextForTemplate( templateFile, skinFile, attributes, decorationModel, project.getName(), locale ); } else { context = siteRenderer.createContextForSkin( skinFile, attributes, decorationModel, project.getName(), locale ); } // Generate static site 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 ); } return context; } /** * Go through the list of reports and process each one like this: *
    *
  • Add the report to a map of reports keyed by filename having the report itself as value *
  • If the report is not yet in the map of documents, add it together with a suitable renderer *
* * @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 locateReports( List reports, Map documents, Locale locale ) { // copy Collection to prevent ConcurrentModificationException List filtered = new ArrayList( reports ); Map 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 displayLanguage = locale.getDisplayLanguage( Locale.ENGLISH ); getLog().info( "Skipped \"" + report.getName( locale ) + "\" report, file \"" + outputName + "\" already exists for the " + displayLanguage + " version." ); reports.remove( mavenReportExecution ); } else { RenderingContext renderingContext = new RenderingContext( siteDirectory, outputName ); 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> categoriseReports( Collection reports ) { Map> categories = new LinkedHashMap>(); for ( MavenReport report : reports ) { List categoryReports = categories.get( report.getCategoryName() ); if ( categoryReports == null ) { categoryReports = new ArrayList(); categories.put( report.getCategoryName(), categoryReports ); } categoryReports.add( report ); } return categories; } protected Map locateDocuments( SiteRenderingContext context, List reports, Locale locale ) throws IOException, RendererException { Map documents = siteRenderer.locateDocumentFiles( context ); Map 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> categories = categoriseReports( reportsByOutputName.values() ); siteTool.populateReportsMenu( context.getDecoration(), locale, categories ); populateReportItems( context.getDecoration(), locale, reportsByOutputName ); if ( categories.containsKey( MavenReport.CATEGORY_PROJECT_INFORMATION ) && generateProjectInfo ) { List categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_INFORMATION ); RenderingContext renderingContext = new RenderingContext( siteDirectory, "project-info.html" ); 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 ) ) { List categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_REPORTS ); RenderingContext renderingContext = new RenderingContext( siteDirectory, "project-reports.html" ); 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; } protected void populateReportItems( DecorationModel decorationModel, Locale locale, Map reportsByOutputName ) { for ( Menu menu : decorationModel.getMenus() ) { populateItemRefs( menu.getItems(), locale, reportsByOutputName ); } } private void populateItemRefs( List items, Locale locale, Map reportsByOutputName ) { for ( Iterator 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 ); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy