Coverage Report - org.apache.maven.plugins.pdf.PdfMojo
 
Classes in this File Line Coverage Branch Coverage Complexity
PdfMojo
0 %
0/422
0 %
0/154
4,75
PdfMojo$PdfXdocSink
0 %
0/8
0 %
0/2
4,75
PdfMojo$ProjectInfoRenderer
0 %
0/51
0 %
0/4
4,75
 
 1  
 package org.apache.maven.plugins.pdf;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  *   http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 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.io.StringWriter;
 28  
 import java.io.Writer;
 29  
 import java.util.ArrayList;
 30  
 import java.util.HashMap;
 31  
 import java.util.List;
 32  
 import java.util.Locale;
 33  
 import java.util.Map;
 34  
 import java.util.Properties;
 35  
 
 36  
 import org.apache.commons.io.input.XmlStreamReader;
 37  
 import org.apache.maven.artifact.Artifact;
 38  
 import org.apache.maven.artifact.repository.ArtifactRepository;
 39  
 import org.apache.maven.artifact.versioning.ArtifactVersion;
 40  
 import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
 41  
 import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
 42  
 import org.apache.maven.artifact.versioning.VersionRange;
 43  
 import org.apache.maven.doxia.Doxia;
 44  
 import org.apache.maven.doxia.docrenderer.AbstractDocumentRenderer;
 45  
 import org.apache.maven.doxia.docrenderer.DocumentRenderer;
 46  
 import org.apache.maven.doxia.docrenderer.DocumentRendererContext;
 47  
 import org.apache.maven.doxia.docrenderer.DocumentRendererException;
 48  
 import org.apache.maven.doxia.docrenderer.pdf.PdfRenderer;
 49  
 import org.apache.maven.doxia.document.DocumentMeta;
 50  
 import org.apache.maven.doxia.document.DocumentModel;
 51  
 import org.apache.maven.doxia.document.DocumentTOCItem;
 52  
 import org.apache.maven.doxia.document.io.xpp3.DocumentXpp3Writer;
 53  
 import org.apache.maven.doxia.index.IndexEntry;
 54  
 import org.apache.maven.doxia.index.IndexingSink;
 55  
 import org.apache.maven.doxia.module.xdoc.XdocSink;
 56  
 import org.apache.maven.doxia.parser.ParseException;
 57  
 import org.apache.maven.doxia.parser.manager.ParserNotFoundException;
 58  
 import org.apache.maven.doxia.sink.Sink;
 59  
 import org.apache.maven.doxia.sink.impl.SinkAdapter;
 60  
 import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
 61  
 import org.apache.maven.doxia.site.decoration.DecorationModel;
 62  
 import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Reader;
 63  
 import org.apache.maven.doxia.siterenderer.Renderer;
 64  
 import org.apache.maven.doxia.siterenderer.RendererException;
 65  
 import org.apache.maven.doxia.siterenderer.SiteRenderingContext;
 66  
 import org.apache.maven.doxia.tools.SiteTool;
 67  
 import org.apache.maven.doxia.tools.SiteToolException;
 68  
 import org.apache.maven.execution.MavenSession;
 69  
 import org.apache.maven.plugin.AbstractMojo;
 70  
 import org.apache.maven.plugin.MojoExecutionException;
 71  
 import org.apache.maven.plugin.MojoFailureException;
 72  
 import org.apache.maven.plugin.PluginManager;
 73  
 import org.apache.maven.plugins.annotations.Component;
 74  
 import org.apache.maven.plugins.annotations.Mojo;
 75  
 import org.apache.maven.plugins.annotations.Parameter;
 76  
 import org.apache.maven.project.MavenProject;
 77  
 import org.apache.maven.project.MavenProjectBuilder;
 78  
 import org.apache.maven.reporting.AbstractMavenReportRenderer;
 79  
 import org.apache.maven.reporting.MavenReport;
 80  
 import org.apache.maven.reporting.MavenReportException;
 81  
 import org.apache.maven.reporting.exec.MavenReportExecution;
 82  
 import org.apache.maven.reporting.exec.MavenReportExecutor;
 83  
 import org.apache.maven.reporting.exec.MavenReportExecutorRequest;
 84  
 import org.apache.maven.settings.Settings;
 85  
 import org.codehaus.plexus.PlexusConstants;
 86  
 import org.codehaus.plexus.PlexusContainer;
 87  
 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
 88  
 import org.codehaus.plexus.context.Context;
 89  
 import org.codehaus.plexus.context.ContextException;
 90  
 import org.codehaus.plexus.i18n.I18N;
 91  
 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
 92  
 import org.codehaus.plexus.util.FileUtils;
 93  
 import org.codehaus.plexus.util.IOUtil;
 94  
 import org.codehaus.plexus.util.PathTool;
 95  
 import org.codehaus.plexus.util.ReaderFactory;
 96  
 import org.codehaus.plexus.util.StringUtils;
 97  
 import org.codehaus.plexus.util.WriterFactory;
 98  
 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
 99  
 
 100  
 /**
 101  
  * Generates a PDF document for a project documentation usually published as web site (with maven-site-plugin).
 102  
  *
 103  
  * @author ltheussl
 104  
  * @version $Id$
 105  
  */
 106  
 @Mojo( name = "pdf", threadSafe = true )
 107  0
 public class PdfMojo
 108  
     extends AbstractMojo implements Contextualizable
 109  
 {
 110  
 
 111  
     /**
 112  
      * The vm line separator
 113  
      */
 114  0
     private static final String EOL = System.getProperty( "line.separator" );
 115  
 
 116  
     // ----------------------------------------------------------------------
 117  
     // Mojo components
 118  
     // ----------------------------------------------------------------------
 119  
 
 120  
     /**
 121  
      * FO Document Renderer.
 122  
      */
 123  
     @Component( hint = "fo" )
 124  
     private PdfRenderer foRenderer;
 125  
 
 126  
     /**
 127  
      * Internationalization.
 128  
      */
 129  
     @Component
 130  
     private I18N i18n;
 131  
 
 132  
     /**
 133  
      * IText Document Renderer.
 134  
      */
 135  
     @Component( hint = "itext" )
 136  
     private PdfRenderer itextRenderer;
 137  
 
 138  
     /**
 139  
      * A comma separated list of locales supported by Maven.
 140  
      * The first valid token will be the default Locale for this instance of the Java Virtual Machine.
 141  
      */
 142  
     @Parameter( property = "locales" )
 143  
     private String locales;
 144  
 
 145  
     /**
 146  
      * Site renderer.
 147  
      */
 148  
     @Component
 149  
     private Renderer siteRenderer;
 150  
 
 151  
     /**
 152  
      * SiteTool.
 153  
      */
 154  
     @Component
 155  
     private SiteTool siteTool;
 156  
 
 157  
     /**
 158  
      * The Plugin manager instance used to resolve Plugin descriptors.
 159  
      *
 160  
      * @since 1.1
 161  
      */
 162  
     @Component( role = PluginManager.class )
 163  
     private PluginManager pluginManager;
 164  
 
 165  
     /**
 166  
      * Doxia.
 167  
      *
 168  
      * @since 1.1
 169  
      */
 170  
     @Component
 171  
     private Doxia doxia;
 172  
 
 173  
     /**
 174  
      * Project builder.
 175  
      *
 176  
      * @since 1.1
 177  
      */
 178  
     @Component
 179  
     private MavenProjectBuilder mavenProjectBuilder;
 180  
 
 181  
     // ----------------------------------------------------------------------
 182  
     // Mojo Parameters
 183  
     // ----------------------------------------------------------------------
 184  
 
 185  
     /**
 186  
      * The Maven Project Object.
 187  
      */
 188  
     @Parameter( defaultValue = "${project}", readonly = true, required = true )
 189  
     private MavenProject project;
 190  
 
 191  
     /**
 192  
      * The Maven Settings.
 193  
      *
 194  
      * @since 1.1
 195  
      */
 196  
     @Parameter( defaultValue = "${settings}", readonly = true, required = true )
 197  
     private Settings settings;
 198  
 
 199  
     /**
 200  
      * The current build session instance.
 201  
      *
 202  
      * @since 1.1
 203  
      */
 204  
     @Parameter( defaultValue = "${session}", readonly = true, required = true )
 205  
     private MavenSession session;
 206  
 
 207  
     /**
 208  
      * Directory containing source for apt, fml and xdoc docs.
 209  
      */
 210  
     @Parameter( defaultValue = "${basedir}/src/site", required = true )
 211  
     private File siteDirectory;
 212  
 
 213  
     /**
 214  
      * Directory containing generated sources for apt, fml and xdoc docs.
 215  
      *
 216  
      * @since 1.1
 217  
      */
 218  
     @Parameter( defaultValue = "${project.build.directory}/generated-site", required = true )
 219  
     private File generatedSiteDirectory;
 220  
 
 221  
     /**
 222  
      * Output directory where PDF files should be created.
 223  
      */
 224  
     @Parameter( defaultValue = "${project.build.directory}/pdf", required = true )
 225  
     private File outputDirectory;
 226  
 
 227  
     /**
 228  
      * Working directory for working files like temp files/resources.
 229  
      */
 230  
     @Parameter( defaultValue = "${project.build.directory}/pdf", required = true )
 231  
     private File workingDirectory;
 232  
 
 233  
     /**
 234  
      * File that contains the DocumentModel of the PDF to generate.
 235  
      */
 236  
     @Parameter( defaultValue = "src/site/pdf.xml" )
 237  
     private File docDescriptor;
 238  
 
 239  
     /**
 240  
      * Identifies the framework to use for pdf generation: either "fo" (default) or "itext".
 241  
      */
 242  
     @Parameter( property = "implementation", defaultValue = "fo", required = true )
 243  
     private String implementation;
 244  
 
 245  
     /**
 246  
      * The local repository.
 247  
      */
 248  
     @Parameter( defaultValue = "${localRepository}", required = true, readonly = true )
 249  
     private ArtifactRepository localRepository;
 250  
 
 251  
     /**
 252  
      * The remote repositories where artifacts are located.
 253  
      *
 254  
      * @since 1.1
 255  
      */
 256  
     @Parameter( defaultValue = "${project.remoteArtifactRepositories}"  )
 257  
     private List<ArtifactRepository> remoteRepositories;
 258  
 
 259  
     /**
 260  
      * If <code>true</false>, aggregate all source documents in one pdf, otherwise generate one pdf for each
 261  
      * source document.
 262  
      */
 263  
     @Parameter( property = "aggregate", defaultValue = "true" )
 264  
     private boolean aggregate;
 265  
 
 266  
     /**
 267  
      * The current version of this plugin.
 268  
      */
 269  
     @Parameter( defaultValue = "${plugin.version}", readonly = true )
 270  
     private String pluginVersion;
 271  
 
 272  
     /**
 273  
      * If <code>true</false>, generate all Maven reports defined in <code>${project.reporting}</code> and append
 274  
      * them as a new entry in the TOC (Table Of Contents).
 275  
      * <b>Note</b>: Including the report generation could fail the PDF generation or increase the build time.
 276  
      *
 277  
      * @since 1.1
 278  
      */
 279  
     @Parameter( property = "includeReports", defaultValue = "true" )
 280  
     private boolean includeReports;
 281  
 
 282  
     /**
 283  
      * Generate a TOC (Table Of Content) for all items defined in the &lt;toc/&gt; element from the document descriptor.
 284  
      * <br/>
 285  
      * Possible values are: 'none', 'start' and 'end'.
 286  
      *
 287  
      * @since 1.1
 288  
      */
 289  
     @Parameter( property = "generateTOC", defaultValue = "start" )
 290  
     private String generateTOC;
 291  
 
 292  
     /**
 293  
      * Whether to validate xml input documents.
 294  
      * If set to true, <strong>all</strong> input documents in xml format
 295  
      * (in particular xdoc and fml) will be validated and any error will
 296  
      * lead to a build failure.
 297  
      *
 298  
      * @since 1.2
 299  
      */
 300  
     @Parameter( property = "validate", defaultValue = "false" )
 301  
     private boolean validate;
 302  
     
 303  
     /**
 304  
      * Reports (Maven 2).
 305  
      *
 306  
      * @since 1.3
 307  
      */
 308  
     @Parameter( defaultValue = "${reports}", required = true, readonly = true )
 309  
     private MavenReport[] reports;
 310  
     
 311  
     /**
 312  
      * Reports (Maven 3).
 313  
      *
 314  
      * @since 1.4
 315  
      */
 316  
     @Parameter( defaultValue = "${project.reporting.plugins}", readonly = true )
 317  
     private org.apache.maven.model.ReportPlugin[] reportingPlugins;
 318  
 
 319  
     // ----------------------------------------------------------------------
 320  
     // Instance fields
 321  
     // ----------------------------------------------------------------------
 322  
 
 323  
     /**
 324  
      * The current document Renderer.
 325  
      * @see #implementation
 326  
      */
 327  
     private DocumentRenderer docRenderer;
 328  
 
 329  
     /**
 330  
      * The default locale.
 331  
      */
 332  
     private Locale defaultLocale;
 333  
 
 334  
     /**
 335  
      * The available locales list.
 336  
      */
 337  
     private List<Locale> localesList;
 338  
 
 339  
     /**
 340  
      * The default decoration model.
 341  
      */
 342  
     private DecorationModel defaultDecorationModel;
 343  
 
 344  
     /**
 345  
      * The temp Site dir to have all site and generated-site files.
 346  
      *
 347  
      * @since 1.1
 348  
      */
 349  
     private File siteDirectoryTmp;
 350  
 
 351  
     /**
 352  
      * The temp Generated Site dir to have generated reports by this plugin.
 353  
      *
 354  
      * @since 1.1
 355  
      */
 356  
     private File generatedSiteDirectoryTmp;
 357  
 
 358  
     /**
 359  
      * A map of generated MavenReport list using locale as key.
 360  
      *
 361  
      * @since 1.1
 362  
      */
 363  
     private Map<Locale, List<MavenReport>> generatedMavenReports;
 364  
     
 365  
     /**
 366  
      * @since 1.3
 367  
      */
 368  
     private PlexusContainer container;
 369  
 
 370  
     // ----------------------------------------------------------------------
 371  
     // Public methods
 372  
     // ----------------------------------------------------------------------
 373  
 
 374  
     /** {@inheritDoc} */
 375  
     public void execute()
 376  
         throws MojoExecutionException, MojoFailureException
 377  
     {
 378  0
         init();
 379  
 
 380  
         try
 381  
         {
 382  0
             generatePdf();
 383  
         }
 384  0
         catch ( IOException e )
 385  
         {
 386  0
             debugLogGeneratedModel( getDocumentModel( Locale.ENGLISH ) );
 387  
 
 388  0
             throw new MojoExecutionException( "Error during document generation: " + e.getMessage(), e );
 389  0
         }
 390  
 
 391  
         try
 392  
         {
 393  0
             copyGeneratedPdf();
 394  
         }
 395  0
         catch ( IOException e )
 396  
         {
 397  0
             throw new MojoExecutionException( "Error copying generated PDF: " + e.getMessage(), e );
 398  0
         }
 399  0
     }
 400  
     
 401  
     /** {@inheritDoc} */
 402  
     public void contextualize( Context context )
 403  
         throws ContextException
 404  
     {
 405  0
         container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
 406  0
     }
 407  
 
 408  
     // ----------------------------------------------------------------------
 409  
     // Private methods
 410  
     // ----------------------------------------------------------------------
 411  
 
 412  
     /**
 413  
      * Init and validate parameters
 414  
      */
 415  
     private void init()
 416  
     {
 417  0
         if ( "fo".equalsIgnoreCase( implementation ) )
 418  
         {
 419  0
             this.docRenderer = foRenderer;
 420  
         }
 421  0
         else if ( "itext".equalsIgnoreCase( implementation ) )
 422  
         {
 423  0
             this.docRenderer = itextRenderer;
 424  
         }
 425  
         else
 426  
         {
 427  0
             getLog().warn( "Invalid 'implementation' parameter: '" + implementation
 428  
                     + "', using 'fo' as default." );
 429  
 
 430  0
             this.docRenderer = foRenderer;
 431  
         }
 432  
 
 433  0
         if ( !( "none".equalsIgnoreCase( generateTOC )
 434  
                 || "start".equalsIgnoreCase( generateTOC ) || "end".equalsIgnoreCase( generateTOC ) ) )
 435  
         {
 436  0
             getLog().warn( "Invalid 'generateTOC' parameter: '" + generateTOC
 437  
                     + "', using 'start' as default." );
 438  
 
 439  0
             this.generateTOC = "start";
 440  
         }
 441  0
     }
 442  
 
 443  
     /**
 444  
      * Copy the generated PDF to outputDirectory.
 445  
      *
 446  
      * @throws MojoExecutionException if any
 447  
      * @throws IOException if any
 448  
      * @since 1.1
 449  
      */
 450  
     private void copyGeneratedPdf()
 451  
         throws MojoExecutionException, IOException
 452  
     {
 453  0
         boolean requireCopy = !outputDirectory.getCanonicalPath().equals( workingDirectory.getCanonicalPath() );
 454  
 
 455  0
         String outputName = getDocumentModel( getDefaultLocale() ).getOutputName().trim();
 456  0
         if ( !outputName.endsWith( ".pdf" ) )
 457  
         {
 458  0
             outputName = outputName.concat( ".pdf" );
 459  
         }
 460  
 
 461  0
         for ( final Locale locale : getAvailableLocales() )
 462  
         {
 463  0
             File generatedPdfSource = new File( getLocaleDirectory( workingDirectory, locale ), outputName );
 464  
 
 465  0
             if ( !generatedPdfSource.exists() )
 466  
             {
 467  0
                 getLog().warn( "Unable to find the generated pdf: " + generatedPdfSource.getAbsolutePath() );
 468  0
                 continue;
 469  
             }
 470  
 
 471  0
             File generatedPdfDest = new File( getLocaleDirectory( outputDirectory, locale ), outputName );
 472  
 
 473  0
             if ( requireCopy )
 474  
             {
 475  0
                 FileUtils.copyFile( generatedPdfSource, generatedPdfDest );
 476  0
                 generatedPdfSource.delete();
 477  
             }
 478  
 
 479  0
             getLog().info( "pdf generated: " + generatedPdfDest );
 480  0
         }
 481  0
     }
 482  
 
 483  
     /**
 484  
      * Generate the PDF.
 485  
      *
 486  
      * @throws MojoExecutionException if any
 487  
      * @throws IOException if any
 488  
      * @since 1.1
 489  
      */
 490  
     private void generatePdf()
 491  
         throws MojoExecutionException, IOException
 492  
     {
 493  0
         Locale.setDefault( getDefaultLocale() );
 494  
 
 495  0
         for ( final Locale locale : getAvailableLocales() )
 496  
         {
 497  0
             final File workingDir = getLocaleDirectory( workingDirectory, locale );
 498  
 
 499  0
             File siteDirectoryFile = getLocaleDirectory( getSiteDirectoryTmp(), locale );
 500  
 
 501  0
             copyResources( locale );
 502  
 
 503  0
             generateMavenReports( locale );
 504  
 
 505  0
             DocumentRendererContext context = new DocumentRendererContext();
 506  0
             context.put( "project", project );
 507  0
             context.put( "settings", settings );
 508  0
             context.put( "PathTool", new PathTool() );
 509  0
             context.put( "FileUtils", new FileUtils() );
 510  0
             context.put( "StringUtils", new StringUtils() );
 511  0
             context.put( "i18n", i18n );
 512  0
             context.put( "generateTOC", generateTOC );
 513  0
             context.put( "validate", validate );
 514  
 
 515  
             // Put any of the properties in directly into the Velocity context
 516  0
             for ( Map.Entry<Object, Object> entry : project.getProperties().entrySet() )
 517  
             {
 518  0
                 context.put( (String) entry.getKey(), entry.getValue() );
 519  0
             }
 520  
             
 521  0
             final DocumentModel model = aggregate ? getDocumentModel( locale ) : null;
 522  
 
 523  
             try
 524  
             {
 525  
                 // TODO use interface see DOXIASITETOOLS-30
 526  0
                 ( (AbstractDocumentRenderer) docRenderer ).render( siteDirectoryFile, workingDir, model, context );
 527  
             }
 528  0
             catch ( DocumentRendererException e )
 529  
             {
 530  0
                 throw new MojoExecutionException( "Error during document generation: " + e.getMessage(), e );
 531  0
             }
 532  0
         }
 533  0
     }
 534  
 
 535  
     /**
 536  
      * @return the default tmpSiteDirectory.
 537  
      * @throws IOException if any
 538  
      * @since 1.1
 539  
      */
 540  
     private File getSiteDirectoryTmp()
 541  
         throws IOException
 542  
     {
 543  0
         if ( this.siteDirectoryTmp == null )
 544  
         {
 545  0
             final File tmpSiteDir = new File( workingDirectory, "site.tmp" );
 546  0
             prepareTempSiteDirectory( tmpSiteDir );
 547  
 
 548  0
             this.siteDirectoryTmp = tmpSiteDir;
 549  
         }
 550  
 
 551  0
         return this.siteDirectoryTmp;
 552  
     }
 553  
 
 554  
     /**
 555  
      * @return the default tmpGeneratedSiteDirectory when report will be created.
 556  
      * @since 1.1
 557  
      */
 558  
     private File getGeneratedSiteDirectoryTmp()
 559  
     {
 560  0
         if ( this.generatedSiteDirectoryTmp == null )
 561  
         {
 562  0
             this.generatedSiteDirectoryTmp = new File( workingDirectory, "generated-site.tmp" );
 563  
         }
 564  
 
 565  0
         return this.generatedSiteDirectoryTmp;
 566  
     }
 567  
 
 568  
     /**
 569  
      * Copy all site and generated-site files in the tmpSiteDirectory.
 570  
      * <br/>
 571  
      * <b>Note</b>: ignore copying of <code>generated-site</code> files if they already exist in the
 572  
      * <code>site</code> dir.
 573  
      *
 574  
      * @param tmpSiteDir not null
 575  
      * @throws IOException if any
 576  
      * @since 1.1
 577  
      */
 578  
     private void prepareTempSiteDirectory( final File tmpSiteDir )
 579  
         throws IOException
 580  
     {
 581  
         // safety
 582  0
         tmpSiteDir.mkdirs();
 583  
 
 584  
         // copy site
 585  0
         if ( siteDirectory.exists() )
 586  
         {
 587  0
             FileUtils.copyDirectoryStructure( siteDirectory, tmpSiteDir );
 588  
         }
 589  
 
 590  
         // Remove SCM files
 591  0
         List<String> files =
 592  
             FileUtils.getFileAndDirectoryNames( tmpSiteDir, FileUtils.getDefaultExcludesAsString(), null, true,
 593  
                                                 true, true, true );
 594  0
         for ( final String fileName : files )
 595  
         {
 596  0
             final File file = new File( fileName );
 597  
 
 598  0
             if ( file.isDirectory() )
 599  
             {
 600  0
                 FileUtils.deleteDirectory( file );
 601  
             }
 602  
             else
 603  
             {
 604  0
                 file.delete();
 605  
             }
 606  0
         }
 607  
 
 608  0
         copySiteDir( generatedSiteDirectory, tmpSiteDir );
 609  0
     }
 610  
 
 611  
     /**
 612  
      * Copy the from site dir to the to dir.
 613  
      *
 614  
      * @param from not null
 615  
      * @param to not null
 616  
      * @throws IOException if any
 617  
      * @since 1.1
 618  
      */
 619  
     private void copySiteDir( final File from, final File to )
 620  
         throws IOException
 621  
     {
 622  0
         if ( from == null || !from.exists() )
 623  
         {
 624  0
             return;
 625  
         }
 626  
 
 627  
         // copy generated-site
 628  0
         for ( final Locale locale : getAvailableLocales() )
 629  
         {
 630  0
             String excludes = getDefaultExcludesWithLocales( getAvailableLocales(), getDefaultLocale() );
 631  0
             List<String> siteFiles = FileUtils.getFileNames( siteDirectory, "**/*", excludes, false );
 632  0
             File siteDirectoryLocale = new File( siteDirectory, locale.getLanguage() );
 633  0
             if ( !locale.getLanguage().equals( getDefaultLocale().getLanguage() ) && siteDirectoryLocale.exists() )
 634  
             {
 635  0
                 siteFiles = FileUtils.getFileNames( siteDirectoryLocale, "**/*", excludes, false );
 636  
             }
 637  
 
 638  0
             List<String> generatedSiteFiles = FileUtils.getFileNames( from, "**/*", excludes, false );
 639  0
             File fromLocale = new File( from, locale.getLanguage() );
 640  0
             if ( !locale.getLanguage().equals( getDefaultLocale().getLanguage() ) && fromLocale.exists() )
 641  
             {
 642  0
                 generatedSiteFiles = FileUtils.getFileNames( fromLocale, "**/*", excludes, false );
 643  
             }
 644  
 
 645  0
             for ( final String generatedSiteFile : generatedSiteFiles )
 646  
             {
 647  0
                 if ( siteFiles.contains( generatedSiteFile ) )
 648  
                 {
 649  0
                     getLog().warn( "Generated-site already contains a file in site: " + generatedSiteFile
 650  
                                        + ". Ignoring copying it!" );
 651  0
                     continue;
 652  
                 }
 653  
 
 654  0
                 if ( !locale.getLanguage().equals( getDefaultLocale().getLanguage() ) )
 655  
                 {
 656  0
                     if ( fromLocale.exists() )
 657  
                     {
 658  0
                         File in = new File( fromLocale, generatedSiteFile );
 659  0
                         File out = new File( new File( to, locale.getLanguage() ), generatedSiteFile );
 660  0
                         out.getParentFile().mkdirs();
 661  0
                         FileUtils.copyFile( in, out );
 662  0
                     }
 663  
                 }
 664  
                 else
 665  
                 {
 666  0
                     File in = new File( from, generatedSiteFile );
 667  0
                     File out = new File( to, generatedSiteFile );
 668  0
                     out.getParentFile().mkdirs();
 669  0
                     FileUtils.copyFile( in, out );
 670  
                 }
 671  0
             }
 672  0
         }
 673  0
     }
 674  
 
 675  
     /**
 676  
      * Constructs a DocumentModel for the current project. The model is either read from
 677  
      * a descriptor file, if it exists, or constructed from information in the pom and site.xml.
 678  
      *
 679  
      * @param locale not null
 680  
      * @return DocumentModel.
 681  
      * @throws MojoExecutionException if any
 682  
      * @see #appendGeneratedReports(DocumentModel, Locale)
 683  
      */
 684  
     private DocumentModel getDocumentModel( Locale locale )
 685  
         throws MojoExecutionException
 686  
     {
 687  0
         if ( docDescriptor.exists() )
 688  
         {
 689  0
             DocumentModel doc = getDocumentModelFromDescriptor( locale );
 690  
             // TODO: descriptor model should get merged into default model, see MODELLO-63
 691  
 
 692  0
             appendGeneratedReports( doc, locale );
 693  
 
 694  0
             return doc;
 695  
         }
 696  
 
 697  0
         DocumentModel model = new DocumentModelBuilder( project, getDefaultDecorationModel() ).getDocumentModel();
 698  
 
 699  0
         model.getMeta().setGenerator( getDefaultGenerator() );
 700  0
         model.getMeta().setLanguage( locale.getLanguage() );
 701  0
         model.getCover().setCoverType( i18n.getString( "pdf-plugin", getDefaultLocale(), "toc.type" ) );
 702  0
         model.getToc().setName( i18n.getString( "pdf-plugin", getDefaultLocale(), "toc.title" ) );
 703  
 
 704  0
         appendGeneratedReports( model, locale );
 705  
 
 706  0
         debugLogGeneratedModel( model );
 707  
 
 708  0
         return model;
 709  
     }
 710  
 
 711  
     /**
 712  
      * Read a DocumentModel from a file.
 713  
      *
 714  
      * @param locale used to set the language.
 715  
      * @return the DocumentModel read from the configured document descriptor.
 716  
      * @throws org.apache.maven.plugin.MojoExecutionException if the model could not be read.
 717  
      */
 718  
     private DocumentModel getDocumentModelFromDescriptor( Locale locale )
 719  
         throws MojoExecutionException
 720  
     {
 721  
         DocumentModel model;
 722  
 
 723  
         try
 724  
         {
 725  0
             model = new DocumentDescriptorReader( project, getLog(),
 726  
                                                   locale ).readAndFilterDocumentDescriptor( docDescriptor );
 727  
         }
 728  0
         catch ( XmlPullParserException ex )
 729  
         {
 730  0
             throw new MojoExecutionException( "Error reading DocumentDescriptor!", ex );
 731  
         }
 732  0
         catch ( IOException io )
 733  
         {
 734  0
             throw new MojoExecutionException( "Error opening DocumentDescriptor!", io );
 735  0
         }
 736  
 
 737  0
         if ( model.getMeta() == null )
 738  
         {
 739  0
             model.setMeta( new DocumentMeta() );
 740  
         }
 741  
 
 742  0
         if ( StringUtils.isEmpty( model.getMeta().getLanguage() ) )
 743  
         {
 744  0
             model.getMeta().setLanguage( locale.getLanguage() );
 745  
         }
 746  
 
 747  0
         if ( StringUtils.isEmpty( model.getMeta().getGenerator() ) )
 748  
         {
 749  0
             model.getMeta().setGenerator( getDefaultGenerator() );
 750  
         }
 751  
 
 752  0
         return model;
 753  
     }
 754  
 
 755  
     /**
 756  
      * Return the directory for a given Locale and the current default Locale.
 757  
      *
 758  
      * @param basedir the base directory
 759  
      * @param locale a Locale.
 760  
      * @return File.
 761  
      */
 762  
     private File getLocaleDirectory( File basedir, Locale locale )
 763  
     {
 764  0
         if ( locale.getLanguage().equals( getDefaultLocale().getLanguage() ) )
 765  
         {
 766  0
             return basedir;
 767  
         }
 768  
 
 769  0
         return new File( basedir, locale.getLanguage() );
 770  
     }
 771  
 
 772  
     /**
 773  
      * @return the default locale from <code>siteTool</code>.
 774  
      * @see #getAvailableLocales()
 775  
      */
 776  
     private Locale getDefaultLocale()
 777  
     {
 778  0
         if ( this.defaultLocale == null )
 779  
         {
 780  0
             this.defaultLocale = getAvailableLocales().get( 0 );
 781  
         }
 782  
 
 783  0
         return this.defaultLocale;
 784  
     }
 785  
 
 786  
     /**
 787  
      * @return the available locales from <code>siteTool</code>.
 788  
      */
 789  
     private List<Locale> getAvailableLocales()
 790  
     {
 791  0
         if ( this.localesList == null )
 792  
         {
 793  0
             this.localesList = siteTool.getSiteLocales( locales );
 794  
         }
 795  
 
 796  0
         return this.localesList;
 797  
     }
 798  
 
 799  
     /**
 800  
      * @return the DecorationModel instance from <code>site.xml</code>
 801  
      * @throws MojoExecutionException if any
 802  
      */
 803  
     private DecorationModel getDefaultDecorationModel()
 804  
         throws MojoExecutionException
 805  
     {
 806  0
         if ( this.defaultDecorationModel == null )
 807  
         {
 808  0
             final Locale locale = getDefaultLocale();
 809  
 
 810  0
             final File descriptorFile = siteTool.getSiteDescriptor( siteDirectory, locale );
 811  0
             DecorationModel decoration = null;
 812  
 
 813  0
             if ( descriptorFile.exists() )
 814  
             {
 815  0
                 XmlStreamReader reader = null;
 816  
                 try
 817  
                 {
 818  0
                     reader = new XmlStreamReader( descriptorFile );
 819  
 
 820  0
                     String siteDescriptorContent = IOUtil.toString( reader );
 821  
 
 822  0
                     reader.close();
 823  0
                     reader = null;
 824  
 
 825  0
                     siteDescriptorContent =
 826  
                         siteTool.getInterpolatedSiteDescriptorContent( new HashMap<String, String>( 2 ), project,
 827  
                                                                        siteDescriptorContent );
 828  
 
 829  0
                     decoration = new DecorationXpp3Reader().read( new StringReader( siteDescriptorContent ) );
 830  
                 }
 831  0
                 catch ( XmlPullParserException e )
 832  
                 {
 833  0
                     throw new MojoExecutionException( "Error parsing site descriptor", e );
 834  
                 }
 835  0
                 catch ( IOException e )
 836  
                 {
 837  0
                     throw new MojoExecutionException( "Error reading site descriptor", e );
 838  
                 }
 839  0
                 catch ( SiteToolException e )
 840  
                 {
 841  0
                     throw new MojoExecutionException( "Error when interpoling site descriptor", e );
 842  
                 }
 843  
                 finally
 844  
                 {
 845  0
                     IOUtil.close( reader );
 846  0
                 }
 847  
             }
 848  
 
 849  0
             this.defaultDecorationModel = decoration;
 850  
         }
 851  
 
 852  0
         return this.defaultDecorationModel;
 853  
     }
 854  
 
 855  
     /**
 856  
      * Parse the decoration model to find the skin artifact and copy its resources to the output dir.
 857  
      *
 858  
      * @param locale not null
 859  
      * @throws MojoExecutionException if any
 860  
      * @see #getDefaultDecorationModel()
 861  
      */
 862  
     private void copyResources( Locale locale )
 863  
         throws MojoExecutionException
 864  
     {
 865  0
         final DecorationModel decorationModel = getDefaultDecorationModel();
 866  0
         if ( decorationModel == null )
 867  
         {
 868  0
             return;
 869  
         }
 870  
 
 871  
         Artifact skinArtifact;
 872  
         try
 873  
         {
 874  0
             skinArtifact =
 875  
                 siteTool.getSkinArtifactFromRepository( localRepository, project.getRemoteArtifactRepositories(),
 876  
                                                         decorationModel );
 877  
         }
 878  0
         catch ( SiteToolException e )
 879  
         {
 880  0
             throw new MojoExecutionException( "SiteToolException: " + e.getMessage(), e );
 881  0
         }
 882  
 
 883  0
         if ( skinArtifact == null )
 884  
         {
 885  0
             return;
 886  
         }
 887  
 
 888  0
         if ( getLog().isDebugEnabled() )
 889  
         {
 890  0
             getLog().debug( "Copy resources from skin artifact: '" + skinArtifact.getId() + "'..." );
 891  
         }
 892  
 
 893  
         try
 894  
         {
 895  0
             final SiteRenderingContext context =
 896  
                 siteRenderer.createContextForSkin( skinArtifact, new HashMap<String, Object>( 2 ), decorationModel,
 897  
                                                    project.getName(), locale );
 898  0
             context.addSiteDirectory( new File( siteDirectory, locale.getLanguage() ) );
 899  
 
 900  0
             siteRenderer.copyResources( context, workingDirectory );
 901  
         }
 902  0
         catch ( IOException e )
 903  
         {
 904  0
             throw new MojoExecutionException( "IOException: " + e.getMessage(), e );
 905  
         }
 906  0
         catch ( RendererException e )
 907  
         {
 908  0
             throw new MojoExecutionException( "RendererException: " + e.getMessage(), e );
 909  0
         }
 910  0
     }
 911  
 
 912  
     /**
 913  
      * Construct a default producer.
 914  
      *
 915  
      * @return A String in the form <code>Maven PDF Plugin v. 1.1.1, 'fo' implementation</code>.
 916  
      */
 917  
     private String getDefaultGenerator()
 918  
     {
 919  0
         return "Maven PDF Plugin v. " + pluginVersion + ", '" + implementation + "' implementation.";
 920  
     }
 921  
 
 922  
     /**
 923  
      * Write the auto-generated model to disc.
 924  
      *
 925  
      * @param docModel the model to write.
 926  
      */
 927  
     private void debugLogGeneratedModel( final DocumentModel docModel )
 928  
     {
 929  0
         if ( getLog().isDebugEnabled() && project != null )
 930  
         {
 931  0
             final File outputDir = new File( project.getBuild().getDirectory(), "pdf" );
 932  
 
 933  0
             if ( !outputDir.exists() )
 934  
             {
 935  0
                 outputDir.mkdirs();
 936  
             }
 937  
 
 938  0
             final File doc = FileUtils.createTempFile( "pdf", ".xml", outputDir );
 939  0
             final DocumentXpp3Writer xpp3 = new DocumentXpp3Writer();
 940  
 
 941  0
             Writer w = null;
 942  
             try
 943  
             {
 944  0
                 w = WriterFactory.newXmlWriter( doc );
 945  0
                 xpp3.write( w, docModel );
 946  0
                 w.close();
 947  0
                 w = null;
 948  0
                 getLog().debug( "Generated a default document model: " + doc.getAbsolutePath() );
 949  
             }
 950  0
             catch ( IOException e )
 951  
             {
 952  0
                 getLog().error( "Failed to write document model: " + e.getMessage() );
 953  0
                 getLog().debug( e );
 954  
             }
 955  
             finally
 956  
             {
 957  0
                 IOUtil.close( w );
 958  0
             }
 959  
         }
 960  0
     }
 961  
 
 962  
     /**
 963  
      * Generate all Maven reports defined in <code>${project.reporting}</code> part
 964  
      * only if <code>generateReports</code> is enabled.
 965  
      *
 966  
      * @param locale not null
 967  
      * @throws MojoExecutionException if any
 968  
      * @throws IOException if any
 969  
      * @since 1.1
 970  
      */
 971  
     private void generateMavenReports( Locale locale )
 972  
         throws MojoExecutionException, IOException
 973  
     {
 974  0
         if ( !includeReports )
 975  
         {
 976  0
             getLog().info( "Skipped report generation." );
 977  0
             return;
 978  
         }
 979  
 
 980  0
         if ( project.getReporting() == null )
 981  
         {
 982  0
             getLog().info( "No report was specified." );
 983  0
             return;
 984  
         }
 985  
 
 986  0
         List<MavenReportExecution> reportExecutions = getReports();
 987  0
         for ( MavenReportExecution reportExecution : reportExecutions )
 988  
         {
 989  0
             generateMavenReport( reportExecution, locale );
 990  0
         }
 991  
 
 992  
         // copy generated site
 993  0
         copySiteDir( getGeneratedSiteDirectoryTmp(), getSiteDirectoryTmp() );
 994  0
         copySiteDir( generatedSiteDirectory, getSiteDirectoryTmp() );
 995  0
     }
 996  
 
 997  
     /**
 998  
      * Generate the given Maven report only if it is not an external report and the report could be generated.
 999  
      *
 1000  
      * @param reportExecution not null
 1001  
      * @param locale not null
 1002  
      * @throws IOException if any
 1003  
      * @throws MojoExecutionException if any
 1004  
      * @since 1.1
 1005  
      */
 1006  
     private void generateMavenReport( MavenReportExecution reportExecution, Locale locale )
 1007  
         throws IOException, MojoExecutionException
 1008  
     {
 1009  0
         MavenReport report = reportExecution.getMavenReport();
 1010  
 
 1011  0
         String localReportName = report.getName( locale );
 1012  
 
 1013  0
         if ( !reportExecution.canGenerateReport() )
 1014  
         {
 1015  0
             getLog().info( "Skipped \"" + localReportName + "\" report." );
 1016  0
             getLog().debug( "canGenerateReport() was false." );
 1017  
 
 1018  0
             return;
 1019  
         }
 1020  
 
 1021  0
         if ( report.isExternalReport() )
 1022  
         {
 1023  0
             getLog().info( "Skipped external \"" + localReportName + "\" report (not supported by pdf plugin)." );
 1024  0
             getLog().debug( "isExternalReport() was false." );
 1025  
 
 1026  0
             return;
 1027  
         }
 1028  
 
 1029  0
         for ( final MavenReport generatedReport : getGeneratedMavenReports( locale ) )
 1030  
         {
 1031  0
             if ( report.getName( locale ).equals( generatedReport.getName( locale ) ) )
 1032  
             {
 1033  0
                 if ( getLog().isDebugEnabled() )
 1034  
                 {
 1035  0
                     getLog().debug( report.getName( locale ) + " was already generated." );
 1036  
                 }
 1037  0
                 return;
 1038  
             }
 1039  0
         }
 1040  
 
 1041  0
         File outDir = new File( getGeneratedSiteDirectoryTmp(), "xdoc" );
 1042  0
         if ( !locale.getLanguage().equals( defaultLocale.getLanguage() ) )
 1043  
         {
 1044  0
             outDir = new File( new File( getGeneratedSiteDirectoryTmp(), locale.getLanguage() ), "xdoc" );
 1045  
         }
 1046  0
         outDir.mkdirs();
 1047  
 
 1048  0
         File generatedReport = new File( outDir, report.getOutputName() + ".xml" );
 1049  
 
 1050  0
         String excludes = getDefaultExcludesWithLocales( getAvailableLocales(), getDefaultLocale() );
 1051  0
         List<String> files =
 1052  
             FileUtils.getFileNames( siteDirectory, "*/" + report.getOutputName() + ".*", excludes, false );
 1053  0
         if ( !locale.getLanguage().equals( defaultLocale.getLanguage() ) )
 1054  
         {
 1055  0
             files =
 1056  
                 FileUtils.getFileNames( new File( siteDirectory, locale.getLanguage() ), "*/"
 1057  
                     + report.getOutputName() + ".*", excludes, false );
 1058  
         }
 1059  
 
 1060  0
         if ( files.size() != 0 )
 1061  
         {
 1062  0
             String displayLanguage = locale.getDisplayLanguage( Locale.ENGLISH );
 1063  
 
 1064  0
             if ( getLog().isInfoEnabled() )
 1065  
             {
 1066  0
                 getLog().info(
 1067  
                                "Skipped \"" + report.getName( locale ) + "\" report, file \""
 1068  
                                    + report.getOutputName() + "\" already exists for the " + displayLanguage
 1069  
                                    + " version." );
 1070  
             }
 1071  
 
 1072  0
             return;
 1073  
         }
 1074  
 
 1075  0
         if ( getLog().isInfoEnabled() )
 1076  
         {
 1077  0
             getLog().info( "Generating \"" + localReportName + "\" report." );
 1078  
         }
 1079  
 
 1080  0
         StringWriter sw = new StringWriter();
 1081  
 
 1082  0
         PdfXdocSink sink = null;
 1083  
         try
 1084  
         {
 1085  0
             sink = new PdfXdocSink( sw );
 1086  0
             renderReportToSink( reportExecution, locale, sink );
 1087  
         }
 1088  0
         catch ( MavenReportException e )
 1089  
         {
 1090  0
             String goal = reportExecution.getPlugin().getArtifactId() + ':' + reportExecution.getPlugin().getVersion()
 1091  
                 + ':' + reportExecution.getGoal();
 1092  0
             throw new MojoExecutionException( "Error generating " + goal + " report", e );
 1093  
         }
 1094  
         finally
 1095  
         {
 1096  0
             if ( sink != null )
 1097  
             {
 1098  0
                 sink.close();
 1099  
             }
 1100  
         }
 1101  
 
 1102  0
         if ( getLog().isDebugEnabled() )
 1103  
         {
 1104  0
             getLog().debug( "Writing generated xdoc to " + generatedReport );
 1105  
         }
 1106  0
         writeGeneratedReport( sw.toString(), generatedReport );
 1107  
 
 1108  
         // keep generated report xdoc only if it is valid
 1109  0
         if ( isValidGeneratedReportXdoc( reportExecution.getPlugin().getId() + ':' + reportExecution.getGoal(),
 1110  
                                          generatedReport, localReportName ) )
 1111  
         {
 1112  0
             getGeneratedMavenReports( locale ).add( report );
 1113  
         }
 1114  0
     }
 1115  
 
 1116  
     /**
 1117  
      * see org.apache.maven.plugins.site.render.ReportDocumentRenderer#renderDocument(...)
 1118  
      *
 1119  
      * @param reportExec
 1120  
      * @param locale
 1121  
      * @param sink
 1122  
      * @throws MavenReportException
 1123  
      */
 1124  
     private void renderReportToSink( MavenReportExecution reportExec, Locale locale, PdfXdocSink sink )
 1125  
         throws MavenReportException
 1126  
     {
 1127  0
         ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
 1128  
         try
 1129  
         {
 1130  0
             if ( reportExec.getClassLoader() != null )
 1131  
             {
 1132  0
                 Thread.currentThread().setContextClassLoader( reportExec.getClassLoader() );
 1133  
             }
 1134  
     
 1135  0
             MavenReport report = reportExec.getMavenReport();
 1136  
     
 1137  
             /*if ( report instanceof MavenMultiPageReport )
 1138  
             {
 1139  
                 // extended multi-page API
 1140  
                 ( (MavenMultiPageReport) report ).generate( mainSink, multiPageSinkFactory, locale );
 1141  
             }
 1142  
             else if ( generateMultiPage( locale, multiPageSinkFactory, mainSink ) )
 1143  
             {
 1144  
                 // extended multi-page API for Maven 2.2, only accessible by reflection API
 1145  
             }
 1146  
             else
 1147  
             {*/
 1148  
             // old single-page-only API
 1149  0
             report.generate( sink, locale );
 1150  
             //}
 1151  
         }
 1152  
         finally
 1153  
         {
 1154  0
             if ( reportExec.getClassLoader() != null )
 1155  
             {
 1156  0
                 Thread.currentThread().setContextClassLoader( originalClassLoader );
 1157  
             }
 1158  
         }
 1159  0
     }
 1160  
 
 1161  
     /**
 1162  
      * @param locale not null
 1163  
      * @return the generated reports
 1164  
      * @since 1.1
 1165  
      */
 1166  
     private List<MavenReport> getGeneratedMavenReports( Locale locale )
 1167  
     {
 1168  0
         if ( this.generatedMavenReports == null )
 1169  
         {
 1170  0
             this.generatedMavenReports = new HashMap<Locale, List<MavenReport>>( 2 );
 1171  
         }
 1172  
 
 1173  0
         if ( this.generatedMavenReports.get( locale ) == null )
 1174  
         {
 1175  0
             this.generatedMavenReports.put( locale, new ArrayList<MavenReport>( 2 ) );
 1176  
         }
 1177  
 
 1178  0
         return this.generatedMavenReports.get( locale );
 1179  
     }
 1180  
 
 1181  
     /**
 1182  
      * Append generated reports to the toc only if <code>generateReports</code> is enabled, for instance:
 1183  
      * <pre>
 1184  
      * &lt;item name="Project Reports" ref="/project-info"&gt;
 1185  
      * &nbsp;&nbsp;&lt;item name="Project License" ref="/license" /&gt;
 1186  
      * &nbsp;&nbsp;&lt;item name="Project Team" ref="/team-list" /&gt;
 1187  
      * &nbsp;&nbsp;&lt;item name="Continuous Integration" ref="/integration" /&gt;
 1188  
      * &nbsp;&nbsp;...
 1189  
      * &lt;/item&gt;
 1190  
      * </pre>
 1191  
      *
 1192  
      * @param model not null
 1193  
      * @param locale not null
 1194  
      * @see #generateMavenReports(Locale)
 1195  
      * @since 1.1
 1196  
      */
 1197  
     private void appendGeneratedReports( DocumentModel model, Locale locale )
 1198  
     {
 1199  0
         if ( !includeReports )
 1200  
         {
 1201  0
             return;
 1202  
         }
 1203  0
         if ( getGeneratedMavenReports( locale ).isEmpty() )
 1204  
         {
 1205  0
             return;
 1206  
         }
 1207  
 
 1208  0
         final DocumentTOCItem documentTOCItem = new DocumentTOCItem();
 1209  0
         documentTOCItem.setName( i18n.getString( "pdf-plugin", locale, "toc.project-info.item" ) );
 1210  0
         documentTOCItem.setRef( "/project-info" ); // see #generateMavenReports(Locale)
 1211  
 
 1212  0
         List<String> addedRef = new ArrayList<String>( 4 );
 1213  
 
 1214  0
         List<DocumentTOCItem> items = new ArrayList<DocumentTOCItem>( 4 );
 1215  
 
 1216  
         // append generated report defined as MavenReport
 1217  0
         for ( final MavenReport report : getGeneratedMavenReports( locale ) )
 1218  
         {
 1219  0
             final DocumentTOCItem reportItem = new DocumentTOCItem();
 1220  0
             reportItem.setName( report.getName( locale ) );
 1221  0
             reportItem.setRef( "/" + report.getOutputName() );
 1222  
 
 1223  0
             items.add( reportItem );
 1224  
 
 1225  0
             addedRef.add( report.getOutputName() );
 1226  0
         }
 1227  
 
 1228  
         // append all generated reports from generated-site
 1229  
         try
 1230  
         {
 1231  0
             if ( generatedSiteDirectory.exists() )
 1232  
             {
 1233  0
                 String excludes = getDefaultExcludesWithLocales( getAvailableLocales(), getDefaultLocale() );
 1234  0
                 List<String> generatedDirs = FileUtils.getDirectoryNames( generatedSiteDirectory, "*", excludes,
 1235  
                                                                           true );
 1236  0
                 if ( !locale.getLanguage().equals( getDefaultLocale().getLanguage() ) )
 1237  
                 {
 1238  0
                     generatedDirs =
 1239  
                         FileUtils.getFileNames( new File( generatedSiteDirectory, locale.getLanguage() ), "*",
 1240  
                                                 excludes, true );
 1241  
                 }
 1242  
 
 1243  0
                 for ( final String generatedDir : generatedDirs )
 1244  
                 {
 1245  0
                     List<String> generatedFiles =
 1246  
                         FileUtils.getFileNames( new File( generatedDir ), "**.*", excludes, false );
 1247  
 
 1248  0
                     for ( final String generatedFile : generatedFiles )
 1249  
                     {
 1250  0
                         final String ref = generatedFile.substring( 0, generatedFile.lastIndexOf( '.' ) );
 1251  
 
 1252  0
                         if ( !addedRef.contains( ref ) )
 1253  
                         {
 1254  0
                             final String title =
 1255  
                                 getGeneratedDocumentTitle( new File( generatedDir, generatedFile ) );
 1256  
 
 1257  0
                             if ( title != null )
 1258  
                             {
 1259  0
                                 final DocumentTOCItem reportItem = new DocumentTOCItem();
 1260  0
                                 reportItem.setName( title );
 1261  0
                                 reportItem.setRef( "/" + ref );
 1262  
 
 1263  0
                                 items.add( reportItem );
 1264  
                             }
 1265  
                         }
 1266  0
                     }
 1267  0
                 }
 1268  
             }
 1269  
         }
 1270  0
         catch ( IOException e )
 1271  
         {
 1272  0
             getLog().error( "IOException: " + e.getMessage() );
 1273  0
             getLog().debug( e );
 1274  0
         }
 1275  
 
 1276  
         // append to Toc
 1277  0
         documentTOCItem.setItems( items );
 1278  0
         model.getToc().addItem( documentTOCItem );
 1279  0
     }
 1280  
 
 1281  
     /**
 1282  
      * Parse a generated Doxia file and returns its title.
 1283  
      *
 1284  
      * @param f not null
 1285  
      * @return the xdoc file title or null if an error occurs.
 1286  
      * @throws IOException if any
 1287  
      * @since 1.1
 1288  
      */
 1289  
     private String getGeneratedDocumentTitle( final File f )
 1290  
         throws IOException
 1291  
     {
 1292  0
         final IndexEntry entry = new IndexEntry( "index" );
 1293  0
         final IndexingSink titleSink = new IndexingSink( entry );
 1294  
 
 1295  0
         Reader reader = null;
 1296  
         try
 1297  
         {
 1298  0
             reader = ReaderFactory.newXmlReader( f );
 1299  
 
 1300  0
             doxia.parse( reader, f.getParentFile().getName(), titleSink );
 1301  
 
 1302  0
             reader.close();
 1303  0
             reader = null;
 1304  
         }
 1305  0
         catch ( ParseException e )
 1306  
         {
 1307  0
             getLog().error( "ParseException: " + e.getMessage() );
 1308  0
             getLog().debug( e );
 1309  0
             return null;
 1310  
         }
 1311  0
         catch ( ParserNotFoundException e )
 1312  
         {
 1313  0
             getLog().error( "ParserNotFoundException: " + e.getMessage() );
 1314  0
             getLog().debug( e );
 1315  0
             return null;
 1316  
         }
 1317  
         finally
 1318  
         {
 1319  0
             IOUtil.close( reader );
 1320  0
         }
 1321  
 
 1322  0
         return titleSink.getTitle();
 1323  
     }
 1324  
 
 1325  
     /**
 1326  
      * Parsing the generated report to see if it is correct or not. Log the error for the user.
 1327  
      *
 1328  
      * @param fullGoal not null
 1329  
      * @param generatedReport not null
 1330  
      * @param localReportName not null
 1331  
      * @return <code>true</code> if Doxia is able to parse the generated report, <code>false</code> otherwise.
 1332  
      * @since 1.1
 1333  
      */
 1334  
     private boolean isValidGeneratedReportXdoc( String fullGoal, File generatedReport, String localReportName )
 1335  
     {
 1336  0
         SinkAdapter sinkAdapter = new SinkAdapter();
 1337  0
         Reader reader = null;
 1338  
         try
 1339  
         {
 1340  0
             reader = ReaderFactory.newXmlReader( generatedReport );
 1341  
 
 1342  0
             doxia.parse( reader, generatedReport.getParentFile().getName(), sinkAdapter );
 1343  
 
 1344  0
             reader.close();
 1345  0
             reader = null;
 1346  
         }
 1347  0
         catch ( ParseException e )
 1348  
         {
 1349  0
             StringBuilder sb = new StringBuilder( 1024 );
 1350  
 
 1351  0
             sb.append( EOL );
 1352  0
             sb.append( "Error when parsing the generated report xdoc file: " );
 1353  0
             sb.append( generatedReport.getAbsolutePath() );
 1354  0
             sb.append( EOL );
 1355  0
             sb.append( e.getMessage() );
 1356  0
             sb.append( EOL );
 1357  
 
 1358  0
             sb.append( "You could:" ).append( EOL );
 1359  0
             sb.append( "  * exclude all reports using -DincludeReports=false" ).append( EOL );
 1360  0
             sb.append( "  * remove the " );
 1361  0
             sb.append( fullGoal );
 1362  0
             sb.append( " from the <reporting/> part. To not affect the site generation, " );
 1363  0
             sb.append( "you could create a PDF profile." );
 1364  
 
 1365  0
             sb.append( EOL ).append( "Ignoring the \"" ).append( localReportName )
 1366  
                     .append( "\" report in the PDF." ).append( EOL );
 1367  
 
 1368  0
             getLog().error( sb.toString() );
 1369  0
             getLog().debug( e );
 1370  
 
 1371  0
             return false;
 1372  
         }
 1373  0
         catch ( ParserNotFoundException e )
 1374  
         {
 1375  0
             getLog().error( "ParserNotFoundException: " + e.getMessage() );
 1376  0
             getLog().debug( e );
 1377  
 
 1378  0
             return false;
 1379  
         }
 1380  0
         catch ( IOException e )
 1381  
         {
 1382  0
             getLog().error( "IOException: " + e.getMessage() );
 1383  0
             getLog().debug( e );
 1384  
 
 1385  0
             return false;
 1386  
         }
 1387  
         finally
 1388  
         {
 1389  0
             IOUtil.close( reader );
 1390  0
         }
 1391  
 
 1392  0
         return true;
 1393  
     }
 1394  
 
 1395  
     protected List<MavenReportExecution> getReports()
 1396  
         throws MojoExecutionException
 1397  
     {
 1398  0
         if ( !isMaven3OrMore() )
 1399  
         {
 1400  0
             getLog().error( "Report generation is not supported with Maven <= 2.x" );
 1401  
         }
 1402  
 
 1403  0
         MavenReportExecutorRequest mavenReportExecutorRequest = new MavenReportExecutorRequest();
 1404  0
         mavenReportExecutorRequest.setLocalRepository( localRepository );
 1405  0
         mavenReportExecutorRequest.setMavenSession( session );
 1406  0
         mavenReportExecutorRequest.setProject( project );
 1407  0
         mavenReportExecutorRequest.setReportPlugins( reportingPlugins );
 1408  
 
 1409  
         MavenReportExecutor mavenReportExecutor;
 1410  
         try
 1411  
         {
 1412  0
             mavenReportExecutor = (MavenReportExecutor) container.lookup( MavenReportExecutor.class.getName() );
 1413  
         }
 1414  0
         catch ( ComponentLookupException e )
 1415  
         {
 1416  0
             throw new MojoExecutionException( "could not get MavenReportExecutor component", e );
 1417  0
         }
 1418  0
         return mavenReportExecutor.buildMavenReports( mavenReportExecutorRequest );
 1419  
     }
 1420  
 
 1421  
     /**
 1422  
      * Check the current Maven version to see if it's Maven 3.0 or newer.
 1423  
      */
 1424  
     protected static boolean isMaven3OrMore()
 1425  
     {
 1426  
         try
 1427  
         {
 1428  0
             ArtifactVersion mavenVersion = new DefaultArtifactVersion( getMavenVersion() );
 1429  0
             return VersionRange.createFromVersionSpec( "[3.0,)" ).containsVersion( mavenVersion );
 1430  
         }
 1431  0
         catch ( InvalidVersionSpecificationException e )
 1432  
         {
 1433  0
             return false;
 1434  
         }
 1435  
 //        return new ComparableVersion( getMavenVersion() ).compareTo( new ComparableVersion( "3.0" ) ) >= 0;
 1436  
     }
 1437  
 
 1438  
     protected static String getMavenVersion()
 1439  
     {
 1440  
         // This relies on the fact that MavenProject is the in core classloader
 1441  
         // and that the core classloader is for the maven-core artifact
 1442  
         // and that should have a pom.properties file
 1443  
         // if this ever changes, we will have to revisit this code.
 1444  0
         final Properties properties = new Properties();
 1445  
 
 1446  0
         InputStream in = null;
 1447  
         try
 1448  
         {
 1449  0
             in = MavenProject.class.getClassLoader().getResourceAsStream( "META-INF/maven/org.apache.maven/maven-core/"
 1450  
                                                                               + "pom.properties" );
 1451  
 
 1452  0
             properties.load( in );
 1453  0
             in.close();
 1454  0
             in = null;
 1455  
         }
 1456  0
         catch ( IOException ioe )
 1457  
         {
 1458  0
             return "";
 1459  
         }
 1460  
         finally
 1461  
         {
 1462  0
             IOUtil.close( in );
 1463  0
         }
 1464  
 
 1465  0
         return properties.getProperty( "version" ).trim();
 1466  
     }
 1467  
 
 1468  
     // ----------------------------------------------------------------------
 1469  
     // static methods
 1470  
     // ----------------------------------------------------------------------
 1471  
 
 1472  
     /**
 1473  
      * Write the given content to the given file.
 1474  
      * <br/>
 1475  
      * <b>Note</b>: try also to fix the content due to some issues in
 1476  
      * {@link org.apache.maven.reporting.AbstractMavenReport}.
 1477  
      *
 1478  
      * @param content the given content
 1479  
      * @param toFile the report file
 1480  
      * @throws IOException if any
 1481  
      * @since 1.1
 1482  
      */
 1483  
     private static void writeGeneratedReport( String content, File toFile )
 1484  
         throws IOException
 1485  
     {
 1486  0
         if ( StringUtils.isEmpty( content ) )
 1487  
         {
 1488  0
             return;
 1489  
         }
 1490  
 
 1491  0
         Writer writer = null;
 1492  
         try
 1493  
         {
 1494  0
             writer = WriterFactory.newXmlWriter( toFile );
 1495  
             // see PdfSink#table()
 1496  0
             writer.write( StringUtils.replace( content, "<table><table", "<table" ) );
 1497  
 
 1498  0
             writer.close();
 1499  0
             writer = null;
 1500  
         }
 1501  
         finally
 1502  
         {
 1503  0
             IOUtil.close( writer );
 1504  0
         }
 1505  0
     }
 1506  
 
 1507  
     /**
 1508  
      * @param locales the list of locales dir to exclude
 1509  
      * @param defaultLocale the default locale.
 1510  
      * @return the comma separated list of default excludes and locales dir.
 1511  
      * @see FileUtils#getDefaultExcludesAsString()
 1512  
      * @since 1.1
 1513  
      */
 1514  
     private static String getDefaultExcludesWithLocales( List<Locale> locales, Locale defaultLocale )
 1515  
     {
 1516  0
         String excludesLocales = FileUtils.getDefaultExcludesAsString();
 1517  0
         for ( final Locale locale : locales )
 1518  
         {
 1519  0
             if ( !locale.getLanguage().equals( defaultLocale.getLanguage() ) )
 1520  
             {
 1521  0
                 excludesLocales = excludesLocales + ",**/" + locale.getLanguage() + "/*";
 1522  
             }
 1523  0
         }
 1524  
 
 1525  0
         return excludesLocales;
 1526  
     }
 1527  
 
 1528  
     // ----------------------------------------------------------------------
 1529  
     // Inner class
 1530  
     // ----------------------------------------------------------------------
 1531  
 
 1532  
     /**
 1533  
      * A sink to generate a Maven report as xdoc with some known workarounds.
 1534  
      *
 1535  
      * @since 1.1
 1536  
      */
 1537  
     private static class PdfXdocSink
 1538  
         extends XdocSink
 1539  
         implements org.codehaus.doxia.sink.Sink
 1540  
     {
 1541  
         protected PdfXdocSink( Writer writer )
 1542  
         {
 1543  0
             super( writer );
 1544  0
         }
 1545  
 
 1546  
         /** {@inheritDoc} */
 1547  
         public void text( String text )
 1548  
         {
 1549  
             // workaround to fix quotes introduced with MPIR-59 (then removed in MPIR-136)
 1550  0
             super.text( StringUtils.replace( text, "\u0092", "'" ) );
 1551  0
         }
 1552  
 
 1553  
         public void tableRow()
 1554  
         {
 1555  
             // To be backward compatible: TODO add to XdocSink
 1556  0
             if ( !this.tableRows )
 1557  
             {
 1558  0
                 tableRows( null, false );
 1559  
             }
 1560  0
             super.tableRow( null );
 1561  0
         }
 1562  
     }
 1563  
 
 1564  
     /**
 1565  
      * Renderer Maven report similar to org.apache.maven.plugins.site.CategorySummaryDocumentRenderer
 1566  
      *
 1567  
      * @since 1.1
 1568  
      */
 1569  0
     private static class ProjectInfoRenderer
 1570  
         extends AbstractMavenReportRenderer
 1571  
     {
 1572  
         private final List<MavenReport> generatedReports;
 1573  
 
 1574  
         private final I18N i18n;
 1575  
 
 1576  
         private final Locale locale;
 1577  
 
 1578  
         ProjectInfoRenderer( Sink sink, List<MavenReport> generatedReports, I18N i18n, Locale locale )
 1579  
         {
 1580  0
             super( sink );
 1581  
 
 1582  0
             this.generatedReports = generatedReports;
 1583  0
             this.i18n = i18n;
 1584  0
             this.locale = locale;
 1585  0
         }
 1586  
 
 1587  
         /** {@inheritDoc} */
 1588  
         public String getTitle()
 1589  
         {
 1590  0
             return i18n.getString( "pdf-plugin", locale, "report.project-info.title" );
 1591  
         }
 1592  
 
 1593  
         /** {@inheritDoc} */
 1594  
         public void renderBody()
 1595  
         {
 1596  0
             sink.section1();
 1597  0
             sink.sectionTitle1();
 1598  0
             sink.text( i18n.getString( "pdf-plugin", locale, "report.project-info.title" ) );
 1599  0
             sink.sectionTitle1_();
 1600  
 
 1601  0
             sink.paragraph();
 1602  0
             sink.text( i18n.getString( "pdf-plugin", locale, "report.project-info.description1" ) + " " );
 1603  0
             sink.link( "http://maven.apache.org" );
 1604  0
             sink.text( "Maven" );
 1605  0
             sink.link_();
 1606  0
             sink.text( " " + i18n.getString( "pdf-plugin", locale, "report.project-info.description2" ) );
 1607  0
             sink.paragraph_();
 1608  
 
 1609  0
             sink.section2();
 1610  0
             sink.sectionTitle2();
 1611  0
             sink.text( i18n.getString( "pdf-plugin", locale, "report.project-info.sectionTitle" ) );
 1612  0
             sink.sectionTitle2_();
 1613  
 
 1614  0
             sink.table();
 1615  
 
 1616  0
             sink.tableRows( new int[] { Sink.JUSTIFY_LEFT, Sink.JUSTIFY_LEFT }, false );
 1617  
 
 1618  0
             String name = i18n.getString( "pdf-plugin", locale, "report.project-info.column.document" );
 1619  0
             String description = i18n.getString( "pdf-plugin", locale, "report.project-info.column.description" );
 1620  
 
 1621  0
             sink.tableRow();
 1622  
 
 1623  0
             sink.tableHeaderCell( SinkEventAttributeSet.CENTER );
 1624  
 
 1625  0
             sink.text( name );
 1626  
 
 1627  0
             sink.tableHeaderCell_();
 1628  
 
 1629  0
             sink.tableHeaderCell( SinkEventAttributeSet.CENTER );
 1630  
 
 1631  0
             sink.text( description );
 1632  
 
 1633  0
             sink.tableHeaderCell_();
 1634  
 
 1635  0
             sink.tableRow_();
 1636  
 
 1637  0
             if ( generatedReports != null )
 1638  
             {
 1639  0
                 for ( final MavenReport report : generatedReports )
 1640  
                 {
 1641  0
                     sink.tableRow();
 1642  0
                     sink.tableCell();
 1643  0
                     sink.link( report.getOutputName() + ".html" );
 1644  0
                     sink.text( report.getName( locale ) );
 1645  0
                     sink.link_();
 1646  0
                     sink.tableCell_();
 1647  0
                     sink.tableCell();
 1648  0
                     sink.text( report.getDescription( locale ) );
 1649  0
                     sink.tableCell_();
 1650  0
                     sink.tableRow_();
 1651  0
                 }
 1652  
             }
 1653  
 
 1654  0
             sink.tableRows_();
 1655  
 
 1656  0
             sink.table_();
 1657  
 
 1658  0
             sink.section2_();
 1659  
 
 1660  0
             sink.section1_();
 1661  0
         }
 1662  
     }
 1663  
 }