Coverage Report - org.apache.maven.plugins.linkcheck.LinkcheckReport
 
Classes in this File Line Coverage Branch Coverage Complexity
LinkcheckReport
0%
0/540
0%
0/154
4,423
 
 1  
 package org.apache.maven.plugins.linkcheck;
 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.FileNotFoundException;
 24  
 import java.io.FileOutputStream;
 25  
 import java.io.IOException;
 26  
 import java.io.OutputStream;
 27  
 import java.io.PrintStream;
 28  
 import java.io.Reader;
 29  
 import java.io.UnsupportedEncodingException;
 30  
 import java.io.Writer;
 31  
 import java.util.ArrayList;
 32  
 import java.util.Arrays;
 33  
 import java.util.Collections;
 34  
 import java.util.Iterator;
 35  
 import java.util.List;
 36  
 import java.util.Locale;
 37  
 import java.util.Properties;
 38  
 
 39  
 import org.apache.commons.io.FilenameUtils;
 40  
 import org.apache.commons.lang.SystemUtils;
 41  
 import org.apache.maven.artifact.repository.ArtifactRepository;
 42  
 import org.apache.maven.doxia.linkcheck.HttpBean;
 43  
 import org.apache.maven.doxia.linkcheck.LinkCheck;
 44  
 import org.apache.maven.doxia.linkcheck.LinkCheckException;
 45  
 import org.apache.maven.doxia.linkcheck.model.LinkcheckFile;
 46  
 import org.apache.maven.doxia.linkcheck.model.LinkcheckFileResult;
 47  
 import org.apache.maven.doxia.linkcheck.model.LinkcheckModel;
 48  
 import org.apache.maven.doxia.siterenderer.Renderer;
 49  
 import org.apache.maven.model.Reporting;
 50  
 import org.apache.maven.plugin.MojoExecutionException;
 51  
 import org.apache.maven.project.MavenProject;
 52  
 import org.apache.maven.reporting.AbstractMavenReport;
 53  
 import org.apache.maven.reporting.MavenReportException;
 54  
 import org.apache.maven.settings.Proxy;
 55  
 import org.apache.maven.settings.Settings;
 56  
 import org.apache.maven.shared.invoker.DefaultInvocationRequest;
 57  
 import org.apache.maven.shared.invoker.DefaultInvoker;
 58  
 import org.apache.maven.shared.invoker.InvocationOutputHandler;
 59  
 import org.apache.maven.shared.invoker.InvocationRequest;
 60  
 import org.apache.maven.shared.invoker.InvocationResult;
 61  
 import org.apache.maven.shared.invoker.Invoker;
 62  
 import org.apache.maven.shared.invoker.MavenInvocationException;
 63  
 import org.apache.maven.shared.invoker.PrintStreamHandler;
 64  
 import org.codehaus.plexus.i18n.I18N;
 65  
 import org.codehaus.plexus.util.FileUtils;
 66  
 import org.codehaus.plexus.util.IOUtil;
 67  
 import org.codehaus.plexus.util.ReaderFactory;
 68  
 import org.codehaus.plexus.util.StringUtils;
 69  
 import org.codehaus.plexus.util.WriterFactory;
 70  
 import org.codehaus.plexus.util.cli.CommandLineUtils;
 71  
 
 72  
 /**
 73  
  * Generates a <code>Linkcheck</code> report.
 74  
  *
 75  
  * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
 76  
  * @version $Id: LinkcheckReport.java 997509 2010-09-15 21:24:28Z dennisl $
 77  
  * @since 1.0
 78  
  * @goal linkcheck
 79  
  */
 80  0
 public class LinkcheckReport
 81  
     extends AbstractMavenReport
 82  
 {
 83  
     // ----------------------------------------------------------------------
 84  
     // Report Components
 85  
     // ----------------------------------------------------------------------
 86  
 
 87  
     /**
 88  
      * Internationalization.
 89  
      *
 90  
      * @component
 91  
      */
 92  
     private I18N i18n;
 93  
 
 94  
     /**
 95  
      * Doxia Site Renderer.
 96  
      *
 97  
      * @component
 98  
      */
 99  
     private Renderer siteRenderer;
 100  
 
 101  
     /**
 102  
      * LinkCheck component.
 103  
      *
 104  
      * @component
 105  
      */
 106  
     private LinkCheck linkCheck;
 107  
 
 108  
     // ----------------------------------------------------------------------
 109  
     // Report Parameters
 110  
     // ----------------------------------------------------------------------
 111  
 
 112  
     /**
 113  
      * The Maven Project.
 114  
      *
 115  
      * @parameter expression="${project}"
 116  
      * @required
 117  
      * @readonly
 118  
      */
 119  
     private MavenProject project;
 120  
 
 121  
     /**
 122  
      * Local Repository.
 123  
      *
 124  
      * @parameter expression="${localRepository}"
 125  
      * @required
 126  
      * @readonly
 127  
      */
 128  
     private ArtifactRepository localRepository;
 129  
 
 130  
     /**
 131  
      * Report output directory.
 132  
      *
 133  
      * @parameter expression="${project.reporting.outputDirectory}"
 134  
      * @required
 135  
      */
 136  
     private File outputDirectory;
 137  
 
 138  
     /**
 139  
      * The Maven Settings.
 140  
      *
 141  
      * @parameter default-value="${settings}"
 142  
      * @required
 143  
      * @readonly
 144  
      */
 145  
     private Settings settings;
 146  
 
 147  
     // ----------------------------------------------------------------------
 148  
     // Linkcheck parameters
 149  
     // ----------------------------------------------------------------------
 150  
 
 151  
     /**
 152  
      * Whether we are offline or not.
 153  
      *
 154  
      * @parameter default-value="${settings.offline}" expression="${linkcheck.offline}"
 155  
      * @required
 156  
      */
 157  
     private boolean offline;
 158  
 
 159  
     /**
 160  
      * If online, the HTTP method should automatically follow HTTP redirects,
 161  
      * <tt>false</tt> otherwise.
 162  
      *
 163  
      * @parameter default-value="true"
 164  
      */
 165  
     private boolean httpFollowRedirect;
 166  
 
 167  
     /**
 168  
      * The location of the Linkcheck cache file.
 169  
      *
 170  
      * @parameter default-value="${project.build.directory}/linkcheck/linkcheck.cache"
 171  
      * @required
 172  
      */
 173  
     protected File linkcheckCache;
 174  
 
 175  
     /**
 176  
      * The location of the Linkcheck report file.
 177  
      *
 178  
      * @parameter default-value="${project.build.directory}/linkcheck/linkcheck.xml"
 179  
      * @required
 180  
      */
 181  
     protected File linkcheckOutput;
 182  
 
 183  
     /**
 184  
      * The HTTP method to use. Currently supported are "GET" and "HEAD".
 185  
      * <dl>
 186  
      * <dt>HTTP GET</dt>
 187  
      * <dd>
 188  
      * The HTTP GET method is defined in section 9.3 of
 189  
      * <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>:
 190  
      * The GET method means retrieve whatever information (in the form of an
 191  
      * entity) is identified by the Request-URI.
 192  
      * </dd>
 193  
      * <dt>HTTP HEAD</dt>
 194  
      * <dd>
 195  
      * The HTTP HEAD method is defined in section 9.4 of
 196  
      * <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>:
 197  
      * The HEAD method is identical to GET except that the server MUST NOT
 198  
      * return a message-body in the response.
 199  
      * </dd>
 200  
      * </dl>
 201  
      *
 202  
      * @parameter default-value="head"
 203  
      * @required
 204  
      */
 205  
     private String httpMethod;
 206  
 
 207  
     /**
 208  
      * The list of HTTP errors to ignored, like <code>404</code>.
 209  
      *
 210  
      * @parameter
 211  
      * @see {@link org.apache.commons.httpclient.HttpStatus} for all defined values.
 212  
      */
 213  
     private int[] excludedHttpStatusErrors;
 214  
 
 215  
     /**
 216  
      * The list of HTTP warnings to ignored, like <code>301</code>.
 217  
      *
 218  
      * @parameter
 219  
      * @see {@link org.apache.commons.httpclient.HttpStatus} for all defined values.
 220  
      */
 221  
     private int[] excludedHttpStatusWarnings;
 222  
 
 223  
     /**
 224  
      * The list of site pages to exclude. By default, this report, i.e. <code>linkcheck.html</code>, will be excluded.
 225  
      * <br/>
 226  
      * <b>Note</b>: No pattern is allowed for excludedPage, only specific file names.
 227  
      *
 228  
      * @parameter
 229  
      */
 230  
     private String[] excludedPages;
 231  
 
 232  
     /**
 233  
      * The list of links to exclude.
 234  
      * <br/>
 235  
      * <b>Note</b>: Patterns like <code>&#42;&#42;/dummy/&#42;</code> are allowed for excludedLink.
 236  
      *
 237  
      * @parameter
 238  
      */
 239  
     private String[] excludedLinks;
 240  
 
 241  
     /**
 242  
      * The file encoding to use when Linkcheck reads the source files. If the property
 243  
      * <code>project.build.sourceEncoding</code> is not set, the platform default encoding is used.
 244  
      *
 245  
      * @parameter expression="${encoding}" default-value="${project.build.sourceEncoding}"
 246  
      */
 247  
     private String encoding;
 248  
 
 249  
     /**
 250  
      * The extra HttpClient parameters to be used when fetching links. For instance:
 251  
      * <pre>
 252  
      * &lt;httpClientParameters&gt;
 253  
      * &nbsp;&lt;property&gt;
 254  
      * &nbsp;&nbsp;&lt;name&gt;http.protocol.max-redirects&lt;/name&gt;
 255  
      * &nbsp;&nbsp;&lt;value&gt;10&lt;/value&gt;
 256  
      * &nbsp;&lt;/property&gt;
 257  
      * &lt;/httpClientParameters&gt;
 258  
      * </pre>
 259  
      * See <a href="http://hc.apache.org/httpclient-3.x/preference-api.html">HttpClient preference page</a>
 260  
      *
 261  
      * @parameter expression="${httpClientParameters}"
 262  
      */
 263  
     private Properties httpClientParameters;
 264  
 
 265  
     /**
 266  
      * Set the timeout to be used when fetching links. A value of zero means the timeout is not used.
 267  
      *
 268  
      * @parameter expression="${timeout}" default-value="2000"
 269  
      */
 270  
     private int timeout;
 271  
 
 272  
     /**
 273  
      * <code>true</code> to skip the report execution, <code>false</code> otherwise.
 274  
      * The purpose is to prevent infinite call when {@link #forceSite} is enable.
 275  
      *
 276  
      * @parameter expression="${linkcheck.skip}" default-value="false"
 277  
      */
 278  
     private boolean skip;
 279  
 
 280  
     /**
 281  
      * <code>true</code> to force the site generation, <code>false</code> otherwise.
 282  
      * Using this parameter ensures that all documents have been correctly generated.
 283  
      *
 284  
      * @parameter expression="${linkcheck.forceSite}" default-value="true"
 285  
      */
 286  
     private boolean forceSite;
 287  
 
 288  
     /**
 289  
      * The base URL to use for absolute links (eg <code>/index.html</code>) in the site.
 290  
      *
 291  
      * @parameter expression="${linkcheck.baseURL}" default-value="${project.url}"
 292  
      */
 293  
     private String baseURL;
 294  
 
 295  
     // ----------------------------------------------------------------------
 296  
     // Instance fields
 297  
     // ----------------------------------------------------------------------
 298  
 
 299  
     /** Result of the linkcheck in {@link #execute()} */
 300  
     private LinkcheckModel result;
 301  
 
 302  
     // ----------------------------------------------------------------------
 303  
     // Public methods
 304  
     // ----------------------------------------------------------------------
 305  
 
 306  
     /** {@inheritDoc} */
 307  
     public String getDescription( Locale locale )
 308  
     {
 309  0
         return i18n.getString( "linkcheck-report", locale, "report.linkcheck.description" );
 310  
     }
 311  
 
 312  
     /** {@inheritDoc} */
 313  
     public String getName( Locale locale )
 314  
     {
 315  0
         return i18n.getString( "linkcheck-report", locale, "report.linkcheck.name" );
 316  
     }
 317  
 
 318  
     /** {@inheritDoc} */
 319  
     public String getOutputName()
 320  
     {
 321  0
         return "linkcheck";
 322  
     }
 323  
 
 324  
     /** {@inheritDoc} */
 325  
     public boolean canGenerateReport()
 326  
     {
 327  0
         if ( skip )
 328  
         {
 329  0
             return false;
 330  
         }
 331  
 
 332  0
         return true;
 333  
     }
 334  
 
 335  
     /** {@inheritDoc} */
 336  
     public void execute()
 337  
         throws MojoExecutionException
 338  
     {
 339  0
         if ( !canGenerateReport() )
 340  
         {
 341  0
             return;
 342  
         }
 343  
 
 344  
         // encoding
 345  0
         if ( StringUtils.isEmpty( encoding ) )
 346  
         {
 347  0
             if ( getLog().isWarnEnabled() )
 348  
             {
 349  0
                 getLog().warn(
 350  
                                "File encoding has not been set, using platform encoding "
 351  
                                    + ReaderFactory.FILE_ENCODING + ", i.e. build is platform dependent!" );
 352  
             }
 353  0
             encoding = ReaderFactory.FILE_ENCODING;
 354  
         }
 355  
 
 356  0
         File tmpReportingOutputDirectory = new File( linkcheckOutput.getParentFile(), "tmpsite" );
 357  0
         tmpReportingOutputDirectory.mkdirs();
 358  
 
 359  
         File basedir;
 360  0
         if ( forceSite )
 361  
         {
 362  0
             basedir = tmpReportingOutputDirectory;
 363  
 
 364  0
             List documents = null;
 365  
             try
 366  
             {
 367  0
                 documents = FileUtils.getFiles( basedir, "**/*.html", null );
 368  
             }
 369  0
             catch ( IOException e )
 370  
             {
 371  0
                 String msg = "IOException: " + e.getMessage();
 372  0
                 if ( getLog().isDebugEnabled() )
 373  
                 {
 374  0
                     getLog().error( msg, e );
 375  
                 }
 376  
                 else
 377  
                 {
 378  0
                     getLog().error( msg );
 379  
                 }
 380  0
             }
 381  
 
 382  
             // if the site was not already generated, invoke it
 383  0
             if ( documents == null || ( documents != null && documents.size() == 0 ) )
 384  
             {
 385  0
                 getLog().info( "Trying to invoke the maven-site-plugin to be sure that all files are generated..." );
 386  
 
 387  
                 try
 388  
                 {
 389  0
                     invokeSite( tmpReportingOutputDirectory );
 390  
                 }
 391  0
                 catch ( IOException e )
 392  
                 {
 393  0
                     throw new MojoExecutionException( "IOException: " + e.getMessage(), e );
 394  0
                 }
 395  
             }
 396  0
         }
 397  
         else
 398  
         {
 399  0
             if ( getLog().isWarnEnabled() )
 400  
             {
 401  0
                 getLog().warn(
 402  
                                "WARRANTY: The number of documents analyzed by Linkcheck could differ with the real "
 403  
                                    + "number of documents!" );
 404  
             }
 405  
 
 406  0
             basedir = outputDirectory;
 407  0
             basedir.mkdirs();
 408  
         }
 409  
 
 410  
         try
 411  
         {
 412  0
             result = executeLinkCheck( basedir );
 413  
         }
 414  0
         catch ( LinkCheckException e )
 415  
         {
 416  0
             throw new MojoExecutionException( "LinkCheckException: " + e.getMessage(), e );
 417  0
         }
 418  0
     }
 419  
 
 420  
     // ----------------------------------------------------------------------
 421  
     // Protected methods
 422  
     // ----------------------------------------------------------------------
 423  
 
 424  
     /** {@inheritDoc} */
 425  
     protected String getOutputDirectory()
 426  
     {
 427  0
         return outputDirectory.getAbsolutePath();
 428  
     }
 429  
 
 430  
     /** {@inheritDoc} */
 431  
     protected MavenProject getProject()
 432  
     {
 433  0
         return project;
 434  
     }
 435  
 
 436  
     /** {@inheritDoc} */
 437  
     protected Renderer getSiteRenderer()
 438  
     {
 439  0
         return siteRenderer;
 440  
     }
 441  
 
 442  
     /** {@inheritDoc} */
 443  
     protected void executeReport( Locale locale )
 444  
         throws MavenReportException
 445  
     {
 446  0
         if ( result == null )
 447  
         {
 448  0
             getLog().debug( "Calling execute()" );
 449  
 
 450  
             try
 451  
             {
 452  0
                 this.execute();
 453  
             }
 454  0
             catch ( MojoExecutionException e )
 455  
             {
 456  0
                 throw new MavenReportException( "MojoExecutionException: " + e.getMessage(), e );
 457  0
             }
 458  
         }
 459  
 
 460  0
         if ( result != null )
 461  
         {
 462  0
             generateReport( locale, result );
 463  
             // free memory
 464  0
             result = null;
 465  
         }
 466  0
     }
 467  
 
 468  
     // ----------------------------------------------------------------------
 469  
     // Private methods
 470  
     // ----------------------------------------------------------------------
 471  
 
 472  
     /**
 473  
      * Execute the <code>Linkcheck</code> tool.
 474  
      *
 475  
      * @param basedir not null
 476  
      * @throws LinkCheckException if any
 477  
      */
 478  
     private LinkcheckModel executeLinkCheck( File basedir )
 479  
         throws LinkCheckException
 480  
     {
 481  
         // Wrap linkcheck
 482  0
         linkCheck.setOnline( !offline );
 483  0
         linkCheck.setBasedir( basedir );
 484  0
         linkCheck.setBaseURL( baseURL );
 485  0
         linkCheck.setReportOutput( linkcheckOutput );
 486  0
         linkCheck.setLinkCheckCache( linkcheckCache );
 487  0
         linkCheck.setExcludedLinks( excludedLinks );
 488  0
         linkCheck.setExcludedPages( getExcludedPages() );
 489  0
         linkCheck.setExcludedHttpStatusErrors( excludedHttpStatusErrors );
 490  0
         linkCheck.setExcludedHttpStatusWarnings( excludedHttpStatusWarnings );
 491  0
         linkCheck.setEncoding( ( StringUtils.isNotEmpty( encoding ) ? encoding : WriterFactory.UTF_8 ) );
 492  
 
 493  0
         HttpBean bean = new HttpBean();
 494  0
         bean.setMethod( httpMethod );
 495  0
         bean.setFollowRedirects( httpFollowRedirect );
 496  0
         bean.setTimeout( timeout );
 497  0
         if ( httpClientParameters != null )
 498  
         {
 499  0
             bean.setHttpClientParameters( httpClientParameters );
 500  
         }
 501  
 
 502  0
         Proxy proxy = settings.getActiveProxy();
 503  0
         if ( proxy != null )
 504  
         {
 505  0
             bean.setProxyHost( proxy.getHost() );
 506  0
             bean.setProxyPort( proxy.getPort() );
 507  0
             bean.setProxyUser( proxy.getUsername() );
 508  0
             bean.setProxyPassword( proxy.getPassword() );
 509  
         }
 510  0
         linkCheck.setHttp( bean );
 511  
 
 512  0
         return linkCheck.execute();
 513  
     }
 514  
 
 515  
     /**
 516  
      * @return the excludedPages defined by the user and also this report.
 517  
      */
 518  
     private String[] getExcludedPages()
 519  
     {
 520  0
         List pagesToExclude =
 521  
             ( excludedPages != null ? new ArrayList( Arrays.asList( excludedPages ) ) : new ArrayList() );
 522  
 
 523  
         // Exclude this report
 524  0
         pagesToExclude.add( getOutputName() + ".html" );
 525  
 
 526  0
         return (String[]) pagesToExclude.toArray( new String[0] );
 527  
     }
 528  
 
 529  
     /**
 530  
      * Invoke Maven for the <code>site</code> phase for a temporary Maven project using
 531  
      * <code>tmpReportingOutputDirectory</code> as <code>${project.reporting.outputDirectory}</code>.
 532  
      * This is a workaround to be sure that all site files have been correctly generated.
 533  
      * <br/>
 534  
      * <b>Note 1</b>: the Maven Home should be defined in the <code>maven.home</code> Java system property
 535  
      * or defined in <code>M2_HOME</code> system env variables.
 536  
      * <b>Note 2</be>: we can't use <code>siteOutputDirectory</code> param from site plugin because some plugins
 537  
      * <code>${project.reporting.outputDirectory}</code> in there conf.
 538  
      *
 539  
      * @param tmpReportingOutputDirectory not null
 540  
      * @throws IOException if any
 541  
      */
 542  
     private void invokeSite( File tmpReportingOutputDirectory )
 543  
         throws IOException
 544  
     {
 545  0
         String mavenHome = getMavenHome();
 546  0
         if ( StringUtils.isEmpty( mavenHome ) )
 547  
         {
 548  0
             if ( getLog().isErrorEnabled() )
 549  
             {
 550  0
                 String msg =
 551  
                     "Could NOT invoke Maven because no Maven Home is defined. You need to have set the M2_HOME "
 552  
                         + "system env variable or a 'maven.home' Java system properties.";
 553  0
                 getLog().error( msg );
 554  
             }
 555  0
             return;
 556  
         }
 557  
 
 558  
         // invoker site parameters
 559  0
         List goals = Collections.singletonList( "site" );
 560  0
         Properties properties = new Properties();
 561  0
         properties.put( "linkcheck.skip", "true" ); // to stop recursion
 562  
 
 563  0
         File invokerLog =
 564  
             FileUtils
 565  
                      .createTempFile( "invoker-site-plugin", ".txt", new File( project.getBuild().getDirectory() ) );
 566  
 
 567  
         // clone project and set a new reporting output dir
 568  
         MavenProject clone;
 569  
         try
 570  
         {
 571  0
             clone = (MavenProject) project.clone();
 572  
         }
 573  0
         catch ( CloneNotSupportedException e )
 574  
         {
 575  0
             IOException ioe = new IOException( "CloneNotSupportedException: " + e.getMessage() );
 576  0
             ioe.setStackTrace( e.getStackTrace() );
 577  0
             throw ioe;
 578  0
         }
 579  
 
 580  
         // MLINKCHECK-1
 581  0
         if ( clone.getOriginalModel().getReporting() == null )
 582  
         {
 583  0
             clone.getOriginalModel().setReporting( new Reporting() );
 584  
         }
 585  
 
 586  0
         clone.getOriginalModel().getReporting().setOutputDirectory( tmpReportingOutputDirectory.getAbsolutePath() );
 587  
 
 588  
         // create the original model as tmp pom file for the invoker
 589  0
         File tmpProjectFile = FileUtils.createTempFile( "pom", ".xml", project.getBasedir() );
 590  0
         Writer writer = null;
 591  
         try
 592  
         {
 593  0
             writer = WriterFactory.newXmlWriter( tmpProjectFile );
 594  0
             clone.writeOriginalModel( writer );
 595  
         }
 596  
         finally
 597  
         {
 598  0
             IOUtil.close( writer );
 599  0
         }
 600  
 
 601  
         // invoke it
 602  
         try
 603  
         {
 604  0
             invoke( tmpProjectFile, invokerLog, mavenHome, goals, properties );
 605  
         }
 606  
         finally
 607  
         {
 608  0
             if ( !getLog().isDebugEnabled() )
 609  
             {
 610  0
                 tmpProjectFile.delete();
 611  
             }
 612  
         }
 613  0
     }
 614  
 
 615  
     /**
 616  
      * @param projectFile not null, should be in the ${project.basedir}
 617  
      * @param invokerLog not null
 618  
      * @param mavenHome not null
 619  
      * @param goals the list of goals
 620  
      * @param properties the properties for the invoker
 621  
      */
 622  
     private void invoke( File projectFile, File invokerLog, String mavenHome, List goals, Properties properties )
 623  
     {
 624  0
         Invoker invoker = new DefaultInvoker();
 625  0
         invoker.setMavenHome( new File( mavenHome ) );
 626  0
         invoker.setLocalRepositoryDirectory( new File( localRepository.getBasedir() ) );
 627  
 
 628  0
         InvocationRequest request = new DefaultInvocationRequest();
 629  0
         request.setBaseDirectory( projectFile.getParentFile() );
 630  0
         request.setPomFile( projectFile );
 631  0
         request.setDebug( getLog().isDebugEnabled() );
 632  0
         request.setGoals( goals );
 633  0
         request.setProperties( properties );
 634  0
         File javaHome = getJavaHome();
 635  0
         if ( javaHome != null )
 636  
         {
 637  0
             request.setJavaHome( javaHome );
 638  
         }
 639  
 
 640  
         InvocationResult invocationResult;
 641  
         try
 642  
         {
 643  0
             if ( getLog().isDebugEnabled() )
 644  
             {
 645  0
                 getLog().debug( "Invoking Maven for the goals: " + goals + " with properties=" + properties );
 646  
             }
 647  0
             invocationResult = invoke( invoker, request, invokerLog, goals, properties, null );
 648  
         }
 649  0
         catch ( MavenInvocationException e )
 650  
         {
 651  0
             if ( getLog().isDebugEnabled() )
 652  
             {
 653  0
                 getLog().error( "MavenInvocationException: " + e.getMessage(), e );
 654  
             }
 655  0
             getLog().error( "Error when invoking Maven, consult the invoker log." );
 656  0
             return;
 657  0
         }
 658  
 
 659  0
         String invokerLogContent = null;
 660  0
         Reader reader = null;
 661  
         try
 662  
         {
 663  0
             reader = ReaderFactory.newReader( invokerLog, "UTF-8" );
 664  0
             invokerLogContent = IOUtil.toString( reader );
 665  
         }
 666  0
         catch ( IOException e )
 667  
         {
 668  0
             String msg = "IOException: " + e.getMessage();
 669  0
             if ( getLog().isDebugEnabled() )
 670  
             {
 671  0
                 getLog().error( msg, e );
 672  
             }
 673  
             else
 674  
             {
 675  0
                 getLog().error( msg );
 676  
             }
 677  
         }
 678  
         finally
 679  
         {
 680  0
             IOUtil.close( reader );
 681  0
         }
 682  
 
 683  0
         if ( invokerLogContent != null
 684  
             && invokerLogContent.indexOf( "Error occurred during initialization of VM" ) != -1 )
 685  
         {
 686  0
             getLog().info( "Error occurred during initialization of VM, try to use an empty MAVEN_OPTS." );
 687  
 
 688  0
             if ( getLog().isDebugEnabled() )
 689  
             {
 690  0
                 getLog().debug( "Reinvoking Maven for the goals: " + goals + " with an empty MAVEN_OPTS" );
 691  
             }
 692  
 
 693  
             try
 694  
             {
 695  0
                 invocationResult = invoke( invoker, request, invokerLog, goals, properties, "" );
 696  
             }
 697  0
             catch ( MavenInvocationException e )
 698  
             {
 699  0
                 if ( getLog().isDebugEnabled() )
 700  
                 {
 701  0
                     getLog().error( "MavenInvocationException: " + e.getMessage(), e );
 702  
                 }
 703  0
                 getLog().error( "Error when reinvoking Maven, consult the invoker log." );
 704  0
                 return;
 705  0
             }
 706  
         }
 707  
 
 708  0
         if ( invocationResult.getExitCode() != 0 )
 709  
         {
 710  0
             if ( getLog().isErrorEnabled() )
 711  
             {
 712  0
                 getLog().error(
 713  
                                 "Error when invoking Maven, consult the invoker log file: "
 714  
                                     + invokerLog.getAbsolutePath() );
 715  
             }
 716  
         }
 717  0
     }
 718  
 
 719  
     /**
 720  
      * @param invoker not null
 721  
      * @param request not null
 722  
      * @param invokerLog not null
 723  
      * @param goals the list of goals
 724  
      * @param properties the properties for the invoker
 725  
      * @param mavenOpts could be null
 726  
      * @return the invocation result
 727  
      * @throws MavenInvocationException if any
 728  
      */
 729  
     private InvocationResult invoke( Invoker invoker, InvocationRequest request, File invokerLog, List goals,
 730  
                                      Properties properties, String mavenOpts )
 731  
         throws MavenInvocationException
 732  
     {
 733  
         PrintStream ps;
 734  0
         OutputStream os = null;
 735  0
         if ( invokerLog != null )
 736  
         {
 737  0
             if ( getLog().isDebugEnabled() )
 738  
             {
 739  0
                 getLog().debug( "Using " + invokerLog.getAbsolutePath() + " to log the invoker" );
 740  
             }
 741  
 
 742  
             try
 743  
             {
 744  0
                 if ( !invokerLog.exists() )
 745  
                 {
 746  0
                     invokerLog.getParentFile().mkdirs();
 747  
                 }
 748  0
                 os = new FileOutputStream( invokerLog );
 749  0
                 ps = new PrintStream( os, true, "UTF-8" );
 750  
             }
 751  0
             catch ( FileNotFoundException e )
 752  
             {
 753  0
                 if ( getLog().isErrorEnabled() )
 754  
                 {
 755  0
                     getLog().error(
 756  
                                     "FileNotFoundException: " + e.getMessage()
 757  
                                         + ". Using System.out to log the invoker." );
 758  
                 }
 759  0
                 ps = System.out;
 760  
             }
 761  0
             catch ( UnsupportedEncodingException e )
 762  
             {
 763  0
                 if ( getLog().isErrorEnabled() )
 764  
                 {
 765  0
                     getLog().error(
 766  
                                     "UnsupportedEncodingException: " + e.getMessage()
 767  
                                         + ". Using System.out to log the invoker." );
 768  
                 }
 769  0
                 ps = System.out;
 770  0
             }
 771  
         }
 772  
         else
 773  
         {
 774  0
             getLog().debug( "Using System.out to log the invoker." );
 775  
 
 776  0
             ps = System.out;
 777  
         }
 778  
 
 779  0
         if ( mavenOpts != null )
 780  
         {
 781  0
             request.setMavenOpts( mavenOpts );
 782  
         }
 783  
 
 784  0
         InvocationOutputHandler outputHandler = new PrintStreamHandler( ps, false );
 785  0
         request.setOutputHandler( outputHandler );
 786  
 
 787  0
         outputHandler.consumeLine( "Invoking Maven for the goals: " + goals + " with properties=" + properties );
 788  0
         outputHandler.consumeLine( "" );
 789  0
         outputHandler.consumeLine( "M2_HOME=" + getMavenHome() );
 790  0
         outputHandler.consumeLine( "MAVEN_OPTS=" + getMavenOpts() );
 791  0
         outputHandler.consumeLine( "JAVA_HOME=" + getJavaHome() );
 792  0
         outputHandler.consumeLine( "JAVA_OPTS=" + getJavaOpts() );
 793  0
         outputHandler.consumeLine( "" );
 794  
 
 795  
         try
 796  
         {
 797  0
             return invoker.execute( request );
 798  
         }
 799  
         finally
 800  
         {
 801  0
             IOUtil.close( os );
 802  0
             ps = null;
 803  
         }
 804  
     }
 805  
 
 806  
     /**
 807  
      * @return the Maven home defined in the <code>maven.home</code> system property or defined
 808  
      * in <code>M2_HOME</code> system env variables or null if never setted.
 809  
      * @see #invoke(Invoker, InvocationRequest, File, List, Properties, String)
 810  
      */
 811  
     private String getMavenHome()
 812  
     {
 813  0
         String mavenHome = System.getProperty( "maven.home" );
 814  0
         if ( mavenHome == null )
 815  
         {
 816  
             try
 817  
             {
 818  0
                 mavenHome = CommandLineUtils.getSystemEnvVars().getProperty( "M2_HOME" );
 819  
             }
 820  0
             catch ( IOException e )
 821  
             {
 822  0
                 String msg = "IOException: " + e.getMessage();
 823  0
                 if ( getLog().isDebugEnabled() )
 824  
                 {
 825  0
                     getLog().error( msg, e );
 826  
                 }
 827  
                 else
 828  
                 {
 829  0
                     getLog().error( msg );
 830  
                 }
 831  0
             }
 832  
         }
 833  
 
 834  0
         File m2Home = new File( mavenHome );
 835  0
         if ( !m2Home.exists() )
 836  
         {
 837  0
             if ( getLog().isErrorEnabled() )
 838  
             {
 839  0
                 getLog().error(
 840  
                                 "Cannot find Maven application directory. Either specify \'maven.home\' "
 841  
                                     + "system property, or M2_HOME environment variable." );
 842  
             }
 843  
         }
 844  
 
 845  0
         return mavenHome;
 846  
     }
 847  
 
 848  
     /**
 849  
      * @return the <code>MAVEN_OPTS</code> env variable value or null if not setted.
 850  
      * @see #invoke(Invoker, InvocationRequest, File, List, Properties, String)
 851  
      */
 852  
     private String getMavenOpts()
 853  
     {
 854  0
         String mavenOpts = null;
 855  
         try
 856  
         {
 857  0
             mavenOpts = CommandLineUtils.getSystemEnvVars().getProperty( "MAVEN_OPTS" );
 858  
         }
 859  0
         catch ( IOException e )
 860  
         {
 861  0
             String msg = "IOException: " + e.getMessage();
 862  0
             if ( getLog().isDebugEnabled() )
 863  
             {
 864  0
                 getLog().error( msg, e );
 865  
             }
 866  
             else
 867  
             {
 868  0
                 getLog().error( msg );
 869  
             }
 870  0
         }
 871  
 
 872  0
         return mavenOpts;
 873  
     }
 874  
 
 875  
     /**
 876  
      * @return the <code>JAVA_HOME</code> from System.getProperty( "java.home" )
 877  
      * By default, <code>System.getProperty( "java.home" ) = JRE_HOME</code> and <code>JRE_HOME</code>
 878  
      * should be in the <code>JDK_HOME</code> or null if not setted.
 879  
      * @see #invoke(Invoker, InvocationRequest, File, List, Properties, String)
 880  
      */
 881  
     private File getJavaHome()
 882  
     {
 883  
         File javaHome;
 884  0
         if ( SystemUtils.IS_OS_MAC_OSX )
 885  
         {
 886  0
             javaHome = SystemUtils.getJavaHome();
 887  
         }
 888  
         else
 889  
         {
 890  0
             javaHome = new File( SystemUtils.getJavaHome(), ".." );
 891  
         }
 892  
 
 893  0
         if ( javaHome == null || !javaHome.exists() )
 894  
         {
 895  
             try
 896  
             {
 897  0
                 javaHome = new File( CommandLineUtils.getSystemEnvVars().getProperty( "JAVA_HOME" ) );
 898  
             }
 899  0
             catch ( IOException e )
 900  
             {
 901  0
                 String msg = "IOException: " + e.getMessage();
 902  0
                 if ( getLog().isDebugEnabled() )
 903  
                 {
 904  0
                     getLog().error( msg, e );
 905  
                 }
 906  
                 else
 907  
                 {
 908  0
                     getLog().error( msg );
 909  
                 }
 910  0
             }
 911  
         }
 912  
 
 913  0
         if ( javaHome == null || !javaHome.exists() )
 914  
         {
 915  0
             if ( getLog().isErrorEnabled() )
 916  
             {
 917  0
                 getLog().error(
 918  
                                 "Cannot find Java application directory. Either specify \'java.home\' "
 919  
                                     + "system property, or JAVA_HOME environment variable." );
 920  
             }
 921  
         }
 922  
 
 923  0
         return javaHome;
 924  
     }
 925  
 
 926  
     /**
 927  
      * @return the <code>JAVA_OPTS</code> env variable value or null if not setted.
 928  
      * @see #invoke(Invoker, InvocationRequest, File, List, Properties, String)
 929  
      */
 930  
     private String getJavaOpts()
 931  
     {
 932  0
         String javaOpts = null;
 933  
         try
 934  
         {
 935  0
             javaOpts = CommandLineUtils.getSystemEnvVars().getProperty( "JAVA_OPTS" );
 936  
         }
 937  0
         catch ( IOException e )
 938  
         {
 939  0
             String msg = "IOException: " + e.getMessage();
 940  0
             if ( getLog().isDebugEnabled() )
 941  
             {
 942  0
                 getLog().error( msg, e );
 943  
             }
 944  
             else
 945  
             {
 946  0
                 getLog().error( msg );
 947  
             }
 948  0
         }
 949  
 
 950  0
         return javaOpts;
 951  
     }
 952  
 
 953  
     // ----------------------------------------------------------------------
 954  
     // Linkcheck report
 955  
     // ----------------------------------------------------------------------
 956  
 
 957  
     private void generateReport( Locale locale, LinkcheckModel linkcheckModel )
 958  
     {
 959  0
         getSink().head();
 960  0
         getSink().title();
 961  0
         getSink().text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.title" ) );
 962  0
         getSink().title_();
 963  0
         getSink().head_();
 964  
 
 965  0
         getSink().body();
 966  
 
 967  0
         if ( linkcheckModel == null )
 968  
         {
 969  0
             getSink().section1();
 970  0
             getSink().sectionTitle1();
 971  0
             getSink().text( getName( locale ) );
 972  0
             getSink().sectionTitle1_();
 973  
 
 974  0
             getSink().paragraph();
 975  0
             getSink().rawText( i18n.getString( "linkcheck-report", locale, "report.linkcheck.empty" ) );
 976  0
             getSink().paragraph_();
 977  
 
 978  0
             getSink().section1_();
 979  
 
 980  0
             getSink().body_();
 981  0
             getSink().flush();
 982  0
             getSink().close();
 983  
 
 984  0
             return;
 985  
         }
 986  
 
 987  
         // Overview
 988  0
         getSink().section1();
 989  0
         getSink().sectionTitle1();
 990  0
         getSink().text( getName( locale ) );
 991  0
         getSink().sectionTitle1_();
 992  
 
 993  0
         getSink().paragraph();
 994  0
         getSink().rawText( i18n.getString( "linkcheck-report", locale, "report.linkcheck.overview" ) );
 995  0
         getSink().paragraph_();
 996  
 
 997  0
         getSink().section1_();
 998  
 
 999  
         // Statistics
 1000  0
         generateSummarySection( locale, linkcheckModel );
 1001  
 
 1002  0
         if ( linkcheckModel.getFiles().size() > 0 )
 1003  
         {
 1004  
             // Details
 1005  0
             generateDetailsSection( locale, linkcheckModel );
 1006  
         }
 1007  
 
 1008  0
         getSink().body_();
 1009  0
         getSink().flush();
 1010  0
         getSink().close();
 1011  
 
 1012  0
         closeReport();
 1013  0
     }
 1014  
 
 1015  
     private void generateSummarySection( Locale locale, LinkcheckModel linkcheckModel )
 1016  
     {
 1017  
         // Calculus
 1018  0
         List linkcheckFiles = linkcheckModel.getFiles();
 1019  
 
 1020  0
         int totalFiles = linkcheckFiles.size();
 1021  
 
 1022  0
         int totalLinks = 0;
 1023  0
         int totalValidLinks = 0;
 1024  0
         int totalErrorLinks = 0;
 1025  0
         int totalWarningLinks = 0;
 1026  0
         for ( Iterator it = linkcheckFiles.iterator(); it.hasNext(); )
 1027  
         {
 1028  0
             LinkcheckFile linkcheckFile = (LinkcheckFile) it.next();
 1029  
 
 1030  0
             totalLinks += linkcheckFile.getNumberOfLinks();
 1031  0
             totalValidLinks += linkcheckFile.getNumberOfLinks( LinkcheckFileResult.VALID_LEVEL );
 1032  0
             totalErrorLinks += linkcheckFile.getNumberOfLinks( LinkcheckFileResult.ERROR_LEVEL );
 1033  0
             totalWarningLinks += linkcheckFile.getNumberOfLinks( LinkcheckFileResult.WARNING_LEVEL );
 1034  0
         }
 1035  
 
 1036  0
         getSink().section1();
 1037  0
         getSink().sectionTitle1();
 1038  0
         getSink().text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary" ) );
 1039  0
         getSink().sectionTitle1_();
 1040  
 
 1041  
         // Summary of the analysis parameters
 1042  0
         getSink().paragraph();
 1043  0
         getSink().rawText( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.overview1" ) );
 1044  0
         getSink().paragraph_();
 1045  
 
 1046  0
         getSink().table();
 1047  
 
 1048  0
         getSink().tableRow();
 1049  0
         getSink().tableHeaderCell();
 1050  0
         getSink().text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.parameter" ) );
 1051  0
         getSink().tableHeaderCell_();
 1052  0
         getSink().tableHeaderCell();
 1053  0
         getSink().text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.value" ) );
 1054  0
         getSink().tableHeaderCell_();
 1055  0
         getSink().tableRow_();
 1056  
 
 1057  0
         getSink().tableRow();
 1058  0
         getSink().tableCell();
 1059  0
         getSink().rawText(
 1060  
                            i18n.getString( "linkcheck-report", locale,
 1061  
                                            "report.linkcheck.summary.table.httpFollowRedirect" ) );
 1062  0
         getSink().tableCell_();
 1063  0
         getSink().tableCell();
 1064  0
         getSink().text( String.valueOf( httpFollowRedirect ) );
 1065  0
         getSink().tableCell_();
 1066  0
         getSink().tableRow_();
 1067  
 
 1068  0
         getSink().tableRow();
 1069  0
         getSink().tableCell();
 1070  0
         getSink()
 1071  
                  .rawText(
 1072  
                            i18n
 1073  
                                .getString( "linkcheck-report", locale, "report.linkcheck.summary.table.httpMethod" ) );
 1074  0
         getSink().tableCell_();
 1075  0
         getSink().tableCell();
 1076  0
         if ( StringUtils.isEmpty( httpMethod ) )
 1077  
         {
 1078  0
             getSink().text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.none" ) );
 1079  
         }
 1080  
         else
 1081  
         {
 1082  0
             getSink().text( httpMethod );
 1083  
         }
 1084  0
         getSink().tableCell_();
 1085  0
         getSink().tableRow_();
 1086  
 
 1087  0
         getSink().tableRow();
 1088  0
         getSink().tableCell();
 1089  0
         getSink().rawText(
 1090  
                            i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.offline" ) );
 1091  0
         getSink().tableCell_();
 1092  0
         getSink().tableCell();
 1093  0
         getSink().text( String.valueOf( offline ) );
 1094  0
         getSink().tableCell_();
 1095  0
         getSink().tableRow_();
 1096  
 
 1097  0
         getSink().tableRow();
 1098  0
         getSink().tableCell();
 1099  0
         getSink().rawText(
 1100  
                            i18n.getString( "linkcheck-report", locale,
 1101  
                                            "report.linkcheck.summary.table.excludedPages" ) );
 1102  0
         getSink().tableCell_();
 1103  0
         getSink().tableCell();
 1104  0
         if ( getExcludedPages() == null || getExcludedPages().length == 0 )
 1105  
         {
 1106  0
             getSink().text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.none" ) );
 1107  
         }
 1108  
         else
 1109  
         {
 1110  0
             getSink().text( StringUtils.join( getExcludedPages(), "," ) );
 1111  
         }
 1112  0
         getSink().tableCell_();
 1113  0
         getSink().tableRow_();
 1114  
 
 1115  0
         getSink().tableRow();
 1116  0
         getSink().tableCell();
 1117  0
         getSink().rawText(
 1118  
                            i18n.getString( "linkcheck-report", locale,
 1119  
                                            "report.linkcheck.summary.table.excludedLinks" ) );
 1120  0
         getSink().tableCell_();
 1121  0
         getSink().tableCell();
 1122  0
         if ( excludedLinks == null || excludedLinks.length == 0 )
 1123  
         {
 1124  0
             getSink().text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.none" ) );
 1125  
         }
 1126  
         else
 1127  
         {
 1128  0
             getSink().text( StringUtils.join( excludedLinks, "," ) );
 1129  
         }
 1130  0
         getSink().tableCell_();
 1131  0
         getSink().tableRow_();
 1132  
 
 1133  0
         getSink().tableRow();
 1134  0
         getSink().tableCell();
 1135  0
         getSink().rawText(
 1136  
                            i18n.getString( "linkcheck-report", locale,
 1137  
                                            "report.linkcheck.summary.table.excludedHttpStatusErrors" ) );
 1138  0
         getSink().tableCell_();
 1139  0
         getSink().tableCell();
 1140  0
         if ( excludedHttpStatusErrors == null || excludedHttpStatusErrors.length == 0 )
 1141  
         {
 1142  0
             getSink().text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.none" ) );
 1143  
         }
 1144  
         else
 1145  
         {
 1146  0
             getSink().text( toString( excludedHttpStatusErrors ) );
 1147  
         }
 1148  0
         getSink().tableCell_();
 1149  0
         getSink().tableRow_();
 1150  
 
 1151  0
         getSink().tableRow();
 1152  0
         getSink().tableCell();
 1153  0
         getSink().rawText(
 1154  
                            i18n.getString( "linkcheck-report", locale,
 1155  
                                            "report.linkcheck.summary.table.excludedHttpStatusWarnings" ) );
 1156  0
         getSink().tableCell_();
 1157  0
         getSink().tableCell();
 1158  0
         if ( excludedHttpStatusWarnings == null || excludedHttpStatusWarnings.length == 0 )
 1159  
         {
 1160  0
             getSink().text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.none" ) );
 1161  
         }
 1162  
         else
 1163  
         {
 1164  0
             getSink().text( toString( excludedHttpStatusWarnings ) );
 1165  
         }
 1166  0
         getSink().tableCell_();
 1167  0
         getSink().tableRow_();
 1168  
 
 1169  0
         getSink().table_();
 1170  
 
 1171  
         // Summary of the checked files
 1172  0
         getSink().paragraph();
 1173  0
         getSink().rawText( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.overview2" ) );
 1174  0
         getSink().paragraph_();
 1175  
 
 1176  0
         getSink().table();
 1177  
 
 1178  
         // Header
 1179  0
         generateTableHeader( locale, false );
 1180  
 
 1181  
         // Content
 1182  0
         getSink().tableRow();
 1183  
 
 1184  0
         getSink().tableCell();
 1185  0
         getSink().bold();
 1186  0
         getSink().text( totalFiles + "" );
 1187  0
         getSink().bold_();
 1188  0
         getSink().tableCell_();
 1189  0
         getSink().tableCell();
 1190  0
         getSink().bold();
 1191  0
         getSink().text( totalLinks + "" );
 1192  0
         getSink().bold_();
 1193  0
         getSink().tableCell_();
 1194  0
         getSink().tableCell();
 1195  0
         getSink().bold();
 1196  0
         getSink().text( String.valueOf( totalValidLinks ) );
 1197  0
         getSink().bold_();
 1198  0
         getSink().tableCell_();
 1199  0
         getSink().tableCell();
 1200  0
         getSink().bold();
 1201  0
         getSink().text( String.valueOf( totalWarningLinks ) );
 1202  0
         getSink().bold_();
 1203  0
         getSink().tableCell_();
 1204  0
         getSink().tableCell();
 1205  0
         getSink().bold();
 1206  0
         getSink().text( String.valueOf( totalErrorLinks ) );
 1207  0
         getSink().bold_();
 1208  0
         getSink().tableCell_();
 1209  
 
 1210  0
         getSink().tableRow_();
 1211  
 
 1212  0
         getSink().table_();
 1213  
 
 1214  0
         getSink().section1_();
 1215  0
     }
 1216  
 
 1217  
     private void generateDetailsSection( Locale locale, LinkcheckModel linkcheckModel )
 1218  
     {
 1219  0
         getSink().section1();
 1220  0
         getSink().sectionTitle1();
 1221  0
         getSink().text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.detail" ) );
 1222  0
         getSink().sectionTitle1_();
 1223  
 
 1224  0
         getSink().paragraph();
 1225  0
         getSink().rawText( i18n.getString( "linkcheck-report", locale, "report.linkcheck.detail.overview" ) );
 1226  0
         getSink().paragraph_();
 1227  
 
 1228  0
         getSink().table();
 1229  
 
 1230  
         // Header
 1231  0
         generateTableHeader( locale, true );
 1232  
 
 1233  
         // Content
 1234  0
         List linkcheckFiles = linkcheckModel.getFiles();
 1235  0
         for ( Iterator it = linkcheckFiles.iterator(); it.hasNext(); )
 1236  
         {
 1237  0
             LinkcheckFile linkcheckFile = (LinkcheckFile) it.next();
 1238  
 
 1239  0
             getSink().tableRow();
 1240  
 
 1241  0
             getSink().tableCell();
 1242  0
             if ( linkcheckFile.getUnsuccessful() == 0 )
 1243  
             {
 1244  0
                 iconValid( locale );
 1245  
             }
 1246  
             else
 1247  
             {
 1248  0
                 iconError( locale );
 1249  
             }
 1250  0
             getSink().tableCell_();
 1251  
 
 1252  
             // tableCell( createLinkPatternedText( linkcheckFile.getRelativePath(), "./"
 1253  
             // + linkcheckFile.getRelativePath() ) );
 1254  0
             getSink().tableCell();
 1255  0
             getSink().link( linkcheckFile.getRelativePath() );
 1256  0
             getSink().text( linkcheckFile.getRelativePath() );
 1257  0
             getSink().link_();
 1258  0
             getSink().tableCell_();
 1259  0
             getSink().tableCell();
 1260  0
             getSink().text( String.valueOf( linkcheckFile.getNumberOfLinks() ) );
 1261  0
             getSink().tableCell_();
 1262  0
             getSink().tableCell();
 1263  0
             getSink().text( String.valueOf( linkcheckFile.getNumberOfLinks( LinkcheckFileResult.VALID_LEVEL ) ) );
 1264  0
             getSink().tableCell_();
 1265  0
             getSink().tableCell();
 1266  0
             getSink().text( String.valueOf( linkcheckFile.getNumberOfLinks( LinkcheckFileResult.WARNING_LEVEL ) ) );
 1267  0
             getSink().tableCell_();
 1268  0
             getSink().tableCell();
 1269  0
             getSink().text( String.valueOf( linkcheckFile.getNumberOfLinks( LinkcheckFileResult.ERROR_LEVEL ) ) );
 1270  0
             getSink().tableCell_();
 1271  
 
 1272  0
             getSink().tableRow_();
 1273  
 
 1274  
             // Detail error
 1275  0
             if ( linkcheckFile.getUnsuccessful() != 0 )
 1276  
             {
 1277  0
                 getSink().tableRow();
 1278  
 
 1279  0
                 getSink().tableCell();
 1280  0
                 getSink().text( "" );
 1281  0
                 getSink().tableCell_();
 1282  
 
 1283  
                 // TODO it is due to DOXIA-78
 1284  0
                 getSink().rawText( "<td colspan=\"5\">" );
 1285  
 
 1286  0
                 getSink().table();
 1287  
 
 1288  0
                 for ( Iterator it2 = linkcheckFile.getResults().iterator(); it2.hasNext(); )
 1289  
                 {
 1290  0
                     LinkcheckFileResult linkcheckFileResult = (LinkcheckFileResult) it2.next();
 1291  
 
 1292  0
                     if ( linkcheckFileResult.getStatusLevel() == LinkcheckFileResult.VALID_LEVEL )
 1293  
                     {
 1294  0
                         continue;
 1295  
                     }
 1296  
 
 1297  0
                     getSink().tableRow();
 1298  
 
 1299  0
                     getSink().tableCell();
 1300  0
                     if ( linkcheckFileResult.getStatusLevel() == LinkcheckFileResult.WARNING_LEVEL )
 1301  
                     {
 1302  0
                         iconWarning( locale );
 1303  
                     }
 1304  0
                     else if ( linkcheckFileResult.getStatusLevel() == LinkcheckFileResult.ERROR_LEVEL )
 1305  
                     {
 1306  0
                         iconError( locale );
 1307  
                     }
 1308  0
                     getSink().tableCell_();
 1309  
 
 1310  0
                     getSink().tableCell();
 1311  0
                     getSink().italic();
 1312  0
                     if ( linkcheckFileResult.getTarget().startsWith( "#" ) )
 1313  
                     {
 1314  0
                         getSink().link( linkcheckFile.getRelativePath() + linkcheckFileResult.getTarget() );
 1315  
                     }
 1316  0
                     else if ( linkcheckFileResult.getTarget().startsWith( "." ) )
 1317  
                     {
 1318  
                         // We need to calculate a correct absolute path here, because target is a relative path
 1319  0
                         String absolutePath = FilenameUtils.getFullPath( linkcheckFile.getRelativePath() )
 1320  
                             + linkcheckFileResult.getTarget();
 1321  0
                         String normalizedPath = FilenameUtils.normalize( absolutePath );
 1322  0
                         if ( normalizedPath == null )
 1323  
                         {
 1324  0
                             normalizedPath = absolutePath;
 1325  
                         }
 1326  0
                         getSink().link( normalizedPath );
 1327  0
                     }
 1328  
                     else
 1329  
                     {
 1330  0
                         getSink().link( linkcheckFileResult.getTarget() );
 1331  
                     }
 1332  
                     // Show the link as it was written to make it easy for
 1333  
                     // the author to find it in the source document
 1334  0
                     getSink().text( linkcheckFileResult.getTarget() );
 1335  0
                     getSink().link_();
 1336  0
                     getSink().text( ": " );
 1337  0
                     getSink().text( linkcheckFileResult.getErrorMessage() );
 1338  0
                     getSink().italic_();
 1339  0
                     getSink().tableCell_();
 1340  
 
 1341  0
                     getSink().tableRow_();
 1342  0
                 }
 1343  
 
 1344  0
                 getSink().table_();
 1345  
 
 1346  0
                 getSink().tableCell_();
 1347  
 
 1348  0
                 getSink().tableRow_();
 1349  
             }
 1350  0
         }
 1351  
 
 1352  0
         getSink().table_();
 1353  
 
 1354  0
         getSink().section1_();
 1355  0
     }
 1356  
 
 1357  
     private void generateTableHeader( Locale locale, boolean detail )
 1358  
     {
 1359  0
         getSink().tableRow();
 1360  0
         if ( detail )
 1361  
         {
 1362  0
             getSink().rawText( "<th rowspan=\"2\">" );
 1363  0
             getSink().text( "" );
 1364  0
             getSink().tableHeaderCell_();
 1365  
         }
 1366  0
         getSink().rawText( "<th rowspan=\"2\">" );
 1367  0
         getSink().text(
 1368  
                         detail ? i18n.getString( "linkcheck-report", locale,
 1369  
                                                  "report.linkcheck.detail.table.documents" )
 1370  
                                         : i18n.getString( "linkcheck-report", locale,
 1371  
                                                           "report.linkcheck.summary.table.documents" ) );
 1372  0
         getSink().tableHeaderCell_();
 1373  
         // TODO it is due to DOXIA-78
 1374  0
         getSink().rawText( "<th colspan=\"4\" align=\"center\">" );
 1375  0
         getSink().text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.table.links" ) );
 1376  0
         getSink().tableHeaderCell_();
 1377  0
         getSink().tableRow_();
 1378  
 
 1379  0
         getSink().tableRow();
 1380  0
         getSink().tableHeaderCell();
 1381  0
         getSink().text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.table.totalLinks" ) );
 1382  0
         getSink().tableHeaderCell_();
 1383  0
         getSink().tableHeaderCell();
 1384  0
         iconValid( locale );
 1385  0
         getSink().tableHeaderCell_();
 1386  0
         getSink().tableHeaderCell();
 1387  0
         iconWarning( locale );
 1388  0
         getSink().tableHeaderCell_();
 1389  0
         getSink().tableHeaderCell();
 1390  0
         iconError( locale );
 1391  0
         getSink().tableHeaderCell_();
 1392  0
         getSink().tableRow_();
 1393  0
     }
 1394  
 
 1395  
     private void iconError( Locale locale )
 1396  
     {
 1397  0
         getSink().figure();
 1398  0
         getSink().figureCaption();
 1399  0
         getSink().text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.icon.error" ) );
 1400  0
         getSink().figureCaption_();
 1401  
         // should be defined in skins
 1402  0
         getSink().figureGraphics( "images/icon_error_sml.gif" );
 1403  0
         getSink().figure_();
 1404  0
     }
 1405  
 
 1406  
     private void iconValid( Locale locale )
 1407  
     {
 1408  0
         getSink().figure();
 1409  0
         getSink().figureCaption();
 1410  0
         getSink().text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.icon.valid" ) );
 1411  0
         getSink().figureCaption_();
 1412  
         // should be defined in skins
 1413  0
         getSink().figureGraphics( "images/icon_success_sml.gif" );
 1414  0
         getSink().figure_();
 1415  0
     }
 1416  
 
 1417  
     private void iconWarning( Locale locale )
 1418  
     {
 1419  0
         getSink().figure();
 1420  0
         getSink().figureCaption();
 1421  0
         getSink().text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.icon.warning" ) );
 1422  0
         getSink().figureCaption_();
 1423  
         // should be defined in skins
 1424  0
         getSink().figureGraphics( "images/icon_warning_sml.gif" );
 1425  0
         getSink().figure_();
 1426  0
     }
 1427  
 
 1428  
     // ----------------------------------------------------------------------
 1429  
     // static methods
 1430  
     // ----------------------------------------------------------------------
 1431  
 
 1432  
     /**
 1433  
      * Similar to {@link Arrays#toString(int[])} in 1.5.
 1434  
      *
 1435  
      * @param a not null
 1436  
      * @return the array comma separated.
 1437  
      */
 1438  
     private static String toString( int[] a )
 1439  
     {
 1440  0
         if ( a == null || a.length == 0 )
 1441  
         {
 1442  0
             return "";
 1443  
         }
 1444  
 
 1445  0
         StringBuffer buf = new StringBuffer();
 1446  0
         buf.append( a[0] );
 1447  
 
 1448  0
         for ( int i = 1; i < a.length; i++ )
 1449  
         {
 1450  0
             buf.append( ", " );
 1451  0
             buf.append( a[i] );
 1452  
         }
 1453  
 
 1454  0
         return buf.toString();
 1455  
     }
 1456  
 }