blob: 6dee59a56919a39bad6f1a769981725809628fe6 [file] [log] [blame]
package org.apache.maven.plugins.linkcheck;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.util.List;
import java.util.Locale;
import org.apache.commons.io.FilenameUtils;
import org.apache.maven.doxia.linkcheck.model.LinkcheckFile;
import org.apache.maven.doxia.linkcheck.model.LinkcheckFileResult;
import org.apache.maven.doxia.linkcheck.model.LinkcheckModel;
import org.apache.maven.doxia.sink.Sink;
import org.codehaus.plexus.i18n.I18N;
import org.codehaus.plexus.util.StringUtils;
/**
* @author ltheussl
* @since 1.1
*/
public class LinkcheckReportGenerator
{
private final I18N i18n;
private String httpMethod;
private boolean offline;
private String[] excludedLinks;
private Integer[] excludedHttpStatusErrors;
private Integer[] excludedHttpStatusWarnings;
private String[] excludedPages;
private boolean httpFollowRedirect;
/**
* @param i18n not null.
*/
public LinkcheckReportGenerator( I18N i18n )
{
this.i18n = i18n;
}
/**
* @param excludedHttpStatusErrors may be null.
*/
public void setExcludedHttpStatusErrors( Integer[] excludedHttpStatusErrors )
{
this.excludedHttpStatusErrors = excludedHttpStatusErrors;
}
/**
* @param excludedHttpStatusWarnings may be null.
*/
public void setExcludedHttpStatusWarnings( Integer[] excludedHttpStatusWarnings )
{
this.excludedHttpStatusWarnings = excludedHttpStatusWarnings;
}
/**
* @param excludedLinks may be null.
*/
public void setExcludedLinks( String[] excludedLinks )
{
this.excludedLinks = excludedLinks;
}
/**
* @param excludedPages may be null.
*/
public void setExcludedPages( String[] excludedPages )
{
this.excludedPages = excludedPages;
}
/**
* @param httpFollowRedirect default is false.
*/
public void setHttpFollowRedirect( boolean httpFollowRedirect )
{
this.httpFollowRedirect = httpFollowRedirect;
}
/**
* @param httpMethod may be null.
*/
public void setHttpMethod( String httpMethod )
{
this.httpMethod = httpMethod;
}
/**
* @param offline default is false.
*/
public void setOffline( boolean offline )
{
this.offline = offline;
}
/**
* Genarate a report for the given LinkcheckModel and emit it into a Sink.
* <strong>Note</strong> that the Sink is flushed and closed.
*
* @param locale not null.
* @param linkcheckModel may be null.
* @param sink not null.
*/
public void generateReport( Locale locale, LinkcheckModel linkcheckModel, Sink sink )
{
String name = i18n.getString( "linkcheck-report", locale, "report.linkcheck.name" );
sink.head();
sink.title();
sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.title" ) );
sink.title_();
sink.head_();
sink.body();
if ( linkcheckModel == null )
{
sink.section1();
sink.sectionTitle1();
sink.text( name );
sink.sectionTitle1_();
sink.paragraph();
sink.rawText( i18n.getString( "linkcheck-report", locale, "report.linkcheck.empty" ) );
sink.paragraph_();
sink.section1_();
sink.body_();
sink.flush();
sink.close();
return;
}
// Overview
sink.section1();
sink.sectionTitle1();
sink.text( name );
sink.sectionTitle1_();
sink.paragraph();
sink.rawText( i18n.getString( "linkcheck-report", locale, "report.linkcheck.overview" ) );
sink.paragraph_();
sink.section1_();
// Statistics
generateSummarySection( locale, linkcheckModel, sink );
if ( linkcheckModel.getFiles().size() > 0 )
{
// Details
generateDetailsSection( locale, linkcheckModel, sink );
}
sink.body_();
sink.flush();
sink.close();
}
private void generateSummarySection( Locale locale, LinkcheckModel linkcheckModel, Sink sink )
{
// CHECKSTYLE_OFF: LineLength
// Calculus
List linkcheckFiles = linkcheckModel.getFiles();
int totalFiles = linkcheckFiles.size();
int totalLinks = 0;
int totalValidLinks = 0;
int totalErrorLinks = 0;
int totalWarningLinks = 0;
for ( Object linkcheckFile1 : linkcheckFiles )
{
LinkcheckFile linkcheckFile = (LinkcheckFile) linkcheckFile1;
totalLinks += linkcheckFile.getNumberOfLinks();
totalValidLinks += linkcheckFile.getNumberOfLinks( LinkcheckFileResult.VALID_LEVEL );
totalErrorLinks += linkcheckFile.getNumberOfLinks( LinkcheckFileResult.ERROR_LEVEL );
totalWarningLinks += linkcheckFile.getNumberOfLinks( LinkcheckFileResult.WARNING_LEVEL );
}
sink.section1();
sink.sectionTitle1();
sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary" ) );
sink.sectionTitle1_();
// Summary of the analysis parameters
sink.paragraph();
sink.rawText( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.overview1" ) );
sink.paragraph_();
sink.table();
sink.tableRow();
sink.tableHeaderCell();
sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.parameter" ) );
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.value" ) );
sink.tableHeaderCell_();
sink.tableRow_();
sink.tableRow();
sink.tableCell();
sink.rawText( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.httpFollowRedirect" ) );
sink.tableCell_();
sink.tableCell();
sink.text( String.valueOf( httpFollowRedirect ) );
sink.tableCell_();
sink.tableRow_();
sink.tableRow();
sink.tableCell();
sink.rawText( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.httpMethod" ) );
sink.tableCell_();
sink.tableCell();
if ( httpMethod == null || httpMethod.isEmpty() )
{
sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.none" ) );
}
else
{
sink.text( httpMethod );
}
sink.tableCell_();
sink.tableRow_();
sink.tableRow();
sink.tableCell();
sink.rawText( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.offline" ) );
sink.tableCell_();
sink.tableCell();
sink.text( String.valueOf( offline ) );
sink.tableCell_();
sink.tableRow_();
sink.tableRow();
sink.tableCell();
sink.rawText( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.excludedPages" ) );
sink.tableCell_();
sink.tableCell();
if ( excludedPages == null || excludedPages.length == 0 )
{
sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.none" ) );
}
else
{
sink.text( StringUtils.join( excludedPages, "," ) );
}
sink.tableCell_();
sink.tableRow_();
sink.tableRow();
sink.tableCell();
sink.rawText( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.excludedLinks" ) );
sink.tableCell_();
sink.tableCell();
if ( excludedLinks == null || excludedLinks.length == 0 )
{
sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.none" ) );
}
else
{
sink.text( StringUtils.join( excludedLinks, "," ) );
}
sink.tableCell_();
sink.tableRow_();
sink.tableRow();
sink.tableCell();
sink.rawText( i18n.getString( "linkcheck-report", locale,
"report.linkcheck.summary.table.excludedHttpStatusErrors" ) );
sink.tableCell_();
sink.tableCell();
if ( excludedHttpStatusErrors == null || excludedHttpStatusErrors.length == 0 )
{
sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.none" ) );
}
else
{
sink.text( toString( excludedHttpStatusErrors ) );
}
sink.tableCell_();
sink.tableRow_();
sink.tableRow();
sink.tableCell();
sink.rawText( i18n.getString( "linkcheck-report", locale,
"report.linkcheck.summary.table.excludedHttpStatusWarnings" ) );
sink.tableCell_();
sink.tableCell();
if ( excludedHttpStatusWarnings == null || excludedHttpStatusWarnings.length == 0 )
{
sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.none" ) );
}
else
{
sink.text( toString( excludedHttpStatusWarnings ) );
}
sink.tableCell_();
sink.tableRow_();
sink.table_();
// Summary of the checked files
sink.paragraph();
sink.rawText( i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.overview2" ) );
sink.paragraph_();
sink.table();
// Header
generateTableHeader( locale, false, sink );
// Content
sink.tableRow();
sink.tableCell();
sink.bold();
sink.text( totalFiles + "" );
sink.bold_();
sink.tableCell_();
sink.tableCell();
sink.bold();
sink.text( totalLinks + "" );
sink.bold_();
sink.tableCell_();
sink.tableCell();
sink.bold();
sink.text( String.valueOf( totalValidLinks ) );
sink.bold_();
sink.tableCell_();
sink.tableCell();
sink.bold();
sink.text( String.valueOf( totalWarningLinks ) );
sink.bold_();
sink.tableCell_();
sink.tableCell();
sink.bold();
sink.text( String.valueOf( totalErrorLinks ) );
sink.bold_();
sink.tableCell_();
sink.tableRow_();
sink.table_();
sink.section1_();
// CHECKSTYLE_ON: LineLength
}
private void generateDetailsSection( Locale locale, LinkcheckModel linkcheckModel, Sink sink )
{
sink.section1();
sink.sectionTitle1();
sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.detail" ) );
sink.sectionTitle1_();
sink.paragraph();
sink.rawText( i18n.getString( "linkcheck-report", locale, "report.linkcheck.detail.overview" ) );
sink.paragraph_();
sink.table();
// Header
generateTableHeader( locale, true, sink );
// Content
List linkcheckFiles = linkcheckModel.getFiles();
for ( Object linkcheckFile1 : linkcheckFiles )
{
LinkcheckFile linkcheckFile = (LinkcheckFile) linkcheckFile1;
sink.tableRow();
sink.tableCell();
if ( linkcheckFile.getUnsuccessful() == 0 )
{
iconValid( locale, sink );
}
else
{
iconError( locale, sink );
}
sink.tableCell_();
// tableCell( createLinkPatternedText( linkcheckFile.getRelativePath(), "./"
// + linkcheckFile.getRelativePath() ) );
sink.tableCell();
sink.link( linkcheckFile.getRelativePath() );
sink.text( linkcheckFile.getRelativePath() );
sink.link_();
sink.tableCell_();
sink.tableCell();
sink.text( String.valueOf( linkcheckFile.getNumberOfLinks() ) );
sink.tableCell_();
sink.tableCell();
sink.text( String.valueOf( linkcheckFile.getNumberOfLinks( LinkcheckFileResult.VALID_LEVEL ) ) );
sink.tableCell_();
sink.tableCell();
sink.text( String.valueOf( linkcheckFile.getNumberOfLinks( LinkcheckFileResult.WARNING_LEVEL ) ) );
sink.tableCell_();
sink.tableCell();
sink.text( String.valueOf( linkcheckFile.getNumberOfLinks( LinkcheckFileResult.ERROR_LEVEL ) ) );
sink.tableCell_();
sink.tableRow_();
// Detail error
if ( linkcheckFile.getUnsuccessful() != 0 )
{
sink.tableRow();
sink.tableCell();
sink.text( "" );
sink.tableCell_();
// TODO it is due to DOXIA-78
sink.rawText( "<td colspan=\"5\">" );
sink.table();
for ( Object o : linkcheckFile.getResults() )
{
LinkcheckFileResult linkcheckFileResult = (LinkcheckFileResult) o;
if ( linkcheckFileResult.getStatusLevel() == LinkcheckFileResult.VALID_LEVEL )
{
continue;
}
sink.tableRow();
sink.tableCell();
if ( linkcheckFileResult.getStatusLevel() == LinkcheckFileResult.WARNING_LEVEL )
{
iconWarning( locale, sink );
}
else if ( linkcheckFileResult.getStatusLevel() == LinkcheckFileResult.ERROR_LEVEL )
{
iconError( locale, sink );
}
sink.tableCell_();
sink.tableCell();
sink.italic();
if ( linkcheckFileResult.getTarget().startsWith( "#" ) )
{
sink.link( linkcheckFile.getRelativePath() + linkcheckFileResult.getTarget() );
}
else if ( linkcheckFileResult.getTarget().startsWith( "." ) )
{
// We need to calculate a correct absolute path here, because target is a relative path
String absolutePath =
FilenameUtils.getFullPath( linkcheckFile.getRelativePath() )
+ linkcheckFileResult.getTarget();
String normalizedPath = FilenameUtils.normalize( absolutePath );
if ( normalizedPath == null )
{
normalizedPath = absolutePath;
}
sink.link( normalizedPath );
}
else
{
sink.link( linkcheckFileResult.getTarget() );
}
// Show the link as it was written to make it easy for
// the author to find it in the source document
sink.text( linkcheckFileResult.getTarget() );
sink.link_();
sink.text( ": " );
sink.text( linkcheckFileResult.getErrorMessage() );
sink.italic_();
sink.tableCell_();
sink.tableRow_();
}
sink.table_();
sink.tableCell_();
sink.tableRow_();
}
}
sink.table_();
sink.section1_();
}
private void generateTableHeader( Locale locale, boolean detail, Sink sink )
{
sink.tableRow();
if ( detail )
{
sink.rawText( "<th rowspan=\"2\">" );
sink.text( "" );
sink.tableHeaderCell_();
}
sink.rawText( "<th rowspan=\"2\">" );
sink.text( detail ? i18n.getString( "linkcheck-report", locale, "report.linkcheck.detail.table.documents" )
: i18n.getString( "linkcheck-report", locale, "report.linkcheck.summary.table.documents" ) );
sink.tableHeaderCell_();
// TODO it is due to DOXIA-78
sink.rawText( "<th colspan=\"4\" align=\"center\">" );
sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.table.links" ) );
sink.tableHeaderCell_();
sink.tableRow_();
sink.tableRow();
sink.tableHeaderCell();
sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.table.totalLinks" ) );
sink.tableHeaderCell_();
sink.tableHeaderCell();
iconValid( locale, sink );
sink.tableHeaderCell_();
sink.tableHeaderCell();
iconWarning( locale, sink );
sink.tableHeaderCell_();
sink.tableHeaderCell();
iconError( locale, sink );
sink.tableHeaderCell_();
sink.tableRow_();
}
private void iconError( Locale locale, Sink sink )
{
sink.figure();
sink.figureCaption();
sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.icon.error" ) );
sink.figureCaption_();
sink.figureGraphics( LinkcheckReport.ICON_ERROR );
sink.figure_();
}
private void iconValid( Locale locale, Sink sink )
{
sink.figure();
sink.figureCaption();
sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.icon.valid" ) );
sink.figureCaption_();
sink.figureGraphics( LinkcheckReport.ICON_SUCCESS );
sink.figure_();
}
private void iconWarning( Locale locale, Sink sink )
{
sink.figure();
sink.figureCaption();
sink.text( i18n.getString( "linkcheck-report", locale, "report.linkcheck.icon.warning" ) );
sink.figureCaption_();
sink.figureGraphics( LinkcheckReport.ICON_WARNING );
sink.figure_();
}
// ----------------------------------------------------------------------
// static methods
// ----------------------------------------------------------------------
/**
* Similar to {@link Arrays#toString(int[])} in 1.5.
*
* @param a not null
* @return the array comma separated.
*/
private static String toString( Object[] a )
{
if ( a == null || a.length == 0 )
{
return "";
}
StringBuilder buf = new StringBuilder();
buf.append( a[0] );
for ( int i = 1; i < a.length; i++ )
{
buf.append( ", " );
buf.append( a[i] );
}
return buf.toString();
}
}