Java tutorial
package org.fusesource.mvnplugins.linkchecker; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import org.apache.commons.io.FilenameUtils; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.doxia.linkcheck.HttpBean; import org.apache.maven.doxia.linkcheck.LinkCheck; import org.apache.maven.doxia.linkcheck.LinkCheckException; 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.siterenderer.Renderer; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.project.MavenProject; import org.apache.maven.reporting.AbstractMavenReport; import org.apache.maven.reporting.MavenReportException; import org.apache.maven.settings.Proxy; import org.apache.maven.settings.Settings; import org.codehaus.plexus.i18n.I18N; import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.ReaderFactory; import org.codehaus.plexus.util.StringUtils; import org.codehaus.plexus.util.WriterFactory; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Properties; /** * Generates a <code>Linkcheck</code> report. * * Based on code from the maven-linkcheck-plugin * * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a> * @goal linkcheck */ public class LinkcheckReport extends AbstractMavenReport { // ---------------------------------------------------------------------- // Report Components // ---------------------------------------------------------------------- /** * Internationalization. * * @component */ private I18N i18n; /** * Doxia Site Renderer. * * @component */ private Renderer siteRenderer; /** * LinkCheck component. * * @component */ private LinkCheck linkCheck; // ---------------------------------------------------------------------- // Report Parameters // ---------------------------------------------------------------------- /** * The Maven Project. * * @parameter expression="${project}" * @required * @readonly */ private MavenProject project; /** * Local Repository. * * @parameter expression="${localRepository}" * @required * @readonly */ private ArtifactRepository localRepository; /** * Report output directory. * * @parameter expression="${project.reporting.outputDirectory}" * @required */ private File outputDirectory; /** * The Maven Settings. * * @parameter default-value="${settings}" * @required * @readonly */ private Settings settings; // ---------------------------------------------------------------------- // Linkcheck parameters // ---------------------------------------------------------------------- /** * Whether we are offline or not. * * @parameter default-value="${settings.offline}" expression="${linkcheck.offline}" * @required */ private boolean offline; /** * If online, the HTTP method should automatically follow HTTP redirects, * <tt>false</tt> otherwise. * * @parameter default-value="true" */ private boolean httpFollowRedirect; /** * The location of the Linkcheck cache file. * * @parameter default-value="${project.build.directory}/linkcheck/linkcheck.cache" * @required */ protected File linkcheckCache; /** * The location of the Linkcheck report file. * * @parameter default-value="${project.build.directory}/linkcheck/linkcheck.xml" * @required */ protected File linkcheckOutput; /** * The HTTP method to use. Currently supported are "GET" and "HEAD". * <dl> * <dt>HTTP GET</dt> * <dd> * The HTTP GET method is defined in section 9.3 of * <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>: * The GET method means retrieve whatever information (in the form of an * entity) is identified by the Request-URI. * </dd> * <dt>HTTP HEAD</dt> * <dd> * The HTTP HEAD method is defined in section 9.4 of * <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>: * The HEAD method is identical to GET except that the server MUST NOT * return a message-body in the response. * </dd> * </dl> * * @parameter default-value="head" * @required */ private String httpMethod; /** * The list of HTTP errors to ignored, like <code>404</code>. * * @parameter * @see {@link org.apache.commons.httpclient.HttpStatus} for all defined values. */ private int[] excludedHttpStatusErrors; /** * The list of HTTP warnings to ignored, like <code>301</code>. * * @parameter * @see {@link org.apache.commons.httpclient.HttpStatus} for all defined values. */ private int[] excludedHttpStatusWarnings; /** * The list of site pages to exclude. By default, this report, i.e. <code>linkcheck.html</code>, will be excluded. * <br/> * <b>Note</b>: No pattern is allowed for excludedPage, only specific file names. * * @parameter */ private String[] excludedPages; /** * The list of links to exclude. * <br/> * <b>Note</b>: Patterns like <code>**/dummy/*</code> are allowed for excludedLink. * * @parameter */ private String[] excludedLinks; /** * The file encoding to use when Linkcheck reads the source files. If the property * <code>project.build.sourceEncoding</code> is not set, the platform default encoding is used. * * @parameter expression="${encoding}" default-value="${project.build.sourceEncoding}" */ private String encoding; /** * The extra HttpClient parameters to be used when fetching links. For instance: * <pre> * <httpClientParameters> * <property> * <name>http.protocol.max-redirects</name> * <value>10</value> * </property> * </httpClientParameters> * </pre> * See <a href="http://hc.apache.org/httpclient-3.x/preference-api.html">HttpClient preference page</a> * * @parameter expression="${httpClientParameters}" */ private Properties httpClientParameters; /** * Set the timeout to be used when fetching links. A value of zero means the timeout is not used. * * @parameter expression="${timeout}" default-value="2000" */ private int timeout; /** * <code>true</code> to skip the report execution, <code>false</code> otherwise. * The purpose is to prevent infinite call when {@link #forceSite} is enable. * * @parameter expression="${linkcheck.skip}" default-value="false" */ private boolean skip; /** * <code>true</code> to force the site generation, <code>false</code> otherwise. * Using this parameter ensures that all documents have been correctly generated. * * @parameter expression="${linkcheck.forceSite}" default-value="true" */ private boolean forceSite; /** * The base URL to use for absolute links (eg <code>/index.html</code>) in the site. * * @parameter expression="${linkcheck.baseURL}" default-value="${project.url}" */ private String baseURL; /** * The base directory of the generated site to link check * * @parameter default-value="${project.build.directory}/sitegen" */ private File basedir; // ---------------------------------------------------------------------- // Instance fields // ---------------------------------------------------------------------- /** Result of the linkcheck in {@link #execute()} */ private LinkcheckModel result; // ---------------------------------------------------------------------- // Public methods // ---------------------------------------------------------------------- /** {@inheritDoc} */ public String getDescription(Locale locale) { return i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.description"); } /** {@inheritDoc} */ public String getName(Locale locale) { return i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.name"); } /** {@inheritDoc} */ public String getOutputName() { return "sitegen-linkcheck"; } /** {@inheritDoc} */ public boolean canGenerateReport() { if (skip) { return false; } return true; } /** {@inheritDoc} */ public void execute() throws MojoExecutionException { if (!canGenerateReport()) { return; } // encoding if (StringUtils.isEmpty(encoding)) { if (getLog().isWarnEnabled()) { getLog().warn("File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING + ", i.e. build is platform dependent!"); } encoding = ReaderFactory.FILE_ENCODING; } List documents = null; try { documents = FileUtils.getFiles(basedir, "**/*.html", null); } catch (IOException e) { String msg = "IOException: " + e.getMessage(); if (getLog().isDebugEnabled()) { getLog().error(msg, e); } else { getLog().error(msg); } } // if the site was not already generated, invoke it if (documents == null || (documents != null && documents.size() == 0)) { throw new MojoExecutionException("No site generated yet."); } try { result = executeLinkCheck(basedir); } catch (LinkCheckException e) { throw new MojoExecutionException("LinkCheckException: " + e.getMessage(), e); } } // ---------------------------------------------------------------------- // Protected methods // ---------------------------------------------------------------------- /** {@inheritDoc} */ protected String getOutputDirectory() { return outputDirectory.getAbsolutePath(); } /** {@inheritDoc} */ protected MavenProject getProject() { return project; } /** {@inheritDoc} */ protected Renderer getSiteRenderer() { return siteRenderer; } /** {@inheritDoc} */ protected void executeReport(Locale locale) throws MavenReportException { if (result == null) { getLog().debug("Calling execute()"); try { this.execute(); } catch (MojoExecutionException e) { throw new MavenReportException("MojoExecutionException: " + e.getMessage(), e); } } if (result != null) { generateReport(locale, result); // free memory result = null; } } // ---------------------------------------------------------------------- // Private methods // ---------------------------------------------------------------------- /** * Execute the <code>Linkcheck</code> tool. * * @param basedir not null * @throws LinkCheckException if any */ private LinkcheckModel executeLinkCheck(File basedir) throws LinkCheckException { // Wrap linkcheck linkCheck.setOnline(!offline); linkCheck.setBasedir(basedir); linkCheck.setBaseURL(baseURL); linkCheck.setReportOutput(linkcheckOutput); linkCheck.setLinkCheckCache(linkcheckCache); linkCheck.setExcludedLinks(excludedLinks); linkCheck.setExcludedPages(getExcludedPages()); linkCheck.setExcludedHttpStatusErrors(excludedHttpStatusErrors); linkCheck.setExcludedHttpStatusWarnings(excludedHttpStatusWarnings); linkCheck.setEncoding((StringUtils.isNotEmpty(encoding) ? encoding : WriterFactory.UTF_8)); HttpBean bean = new HttpBean(); bean.setMethod(httpMethod); bean.setFollowRedirects(httpFollowRedirect); bean.setTimeout(timeout); if (httpClientParameters != null) { bean.setHttpClientParameters(httpClientParameters); } Proxy proxy = settings.getActiveProxy(); if (proxy != null) { bean.setProxyHost(proxy.getHost()); bean.setProxyPort(proxy.getPort()); bean.setProxyUser(proxy.getUsername()); bean.setProxyPassword(proxy.getPassword()); } linkCheck.setHttp(bean); return linkCheck.execute(); } /** * @return the excludedPages defined by the user and also this report. */ private String[] getExcludedPages() { List pagesToExclude = (excludedPages != null ? new ArrayList(Arrays.asList(excludedPages)) : new ArrayList()); // Exclude this report pagesToExclude.add(getOutputName() + ".html"); return (String[]) pagesToExclude.toArray(new String[0]); } // ---------------------------------------------------------------------- // Linkcheck report // ---------------------------------------------------------------------- private void generateReport(Locale locale, LinkcheckModel linkcheckModel) { getSink().head(); getSink().title(); getSink().text(i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.title")); getSink().title_(); getSink().head_(); getSink().body(); if (linkcheckModel == null) { getSink().section1(); getSink().sectionTitle1(); getSink().text(getName(locale)); getSink().sectionTitle1_(); getSink().paragraph(); getSink().rawText(i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.empty")); getSink().paragraph_(); getSink().section1_(); getSink().body_(); getSink().flush(); getSink().close(); return; } // Overview getSink().section1(); getSink().sectionTitle1(); getSink().text(getName(locale)); getSink().sectionTitle1_(); getSink().paragraph(); getSink().rawText(i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.overview")); getSink().paragraph_(); getSink().section1_(); // Statistics generateSummarySection(locale, linkcheckModel); if (linkcheckModel.getFiles().size() > 0) { // Details generateDetailsSection(locale, linkcheckModel); } getSink().body_(); getSink().flush(); getSink().close(); closeReport(); } private void generateSummarySection(Locale locale, LinkcheckModel linkcheckModel) { // Calculus List linkcheckFiles = linkcheckModel.getFiles(); int totalFiles = linkcheckFiles.size(); int totalLinks = 0; int totalValidLinks = 0; int totalErrorLinks = 0; int totalWarningLinks = 0; for (Iterator it = linkcheckFiles.iterator(); it.hasNext();) { LinkcheckFile linkcheckFile = (LinkcheckFile) it.next(); totalLinks += linkcheckFile.getNumberOfLinks(); totalValidLinks += linkcheckFile.getNumberOfLinks(LinkcheckFileResult.VALID_LEVEL); totalErrorLinks += linkcheckFile.getNumberOfLinks(LinkcheckFileResult.ERROR_LEVEL); totalWarningLinks += linkcheckFile.getNumberOfLinks(LinkcheckFileResult.WARNING_LEVEL); } getSink().section1(); getSink().sectionTitle1(); getSink().text(i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.summary")); getSink().sectionTitle1_(); // Summary of the analysis parameters getSink().paragraph(); getSink().rawText( i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.summary.overview1")); getSink().paragraph_(); getSink().table(); getSink().tableRow(); getSink().tableHeaderCell(); getSink().text(i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.summary.table.parameter")); getSink().tableHeaderCell_(); getSink().tableHeaderCell(); getSink().text( i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.summary.table.value")); getSink().tableHeaderCell_(); getSink().tableRow_(); getSink().tableRow(); getSink().tableCell(); getSink().rawText(i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.summary.table.httpFollowRedirect")); getSink().tableCell_(); getSink().tableCell(); getSink().text(String.valueOf(httpFollowRedirect)); getSink().tableCell_(); getSink().tableRow_(); getSink().tableRow(); getSink().tableCell(); getSink().rawText(i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.summary.table.httpMethod")); getSink().tableCell_(); getSink().tableCell(); if (StringUtils.isEmpty(httpMethod)) { getSink().text(i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.summary.table.none")); } else { getSink().text(httpMethod); } getSink().tableCell_(); getSink().tableRow_(); getSink().tableRow(); getSink().tableCell(); getSink().rawText(i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.summary.table.offline")); getSink().tableCell_(); getSink().tableCell(); getSink().text(String.valueOf(offline)); getSink().tableCell_(); getSink().tableRow_(); getSink().tableRow(); getSink().tableCell(); getSink().rawText(i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.summary.table.excludedPages")); getSink().tableCell_(); getSink().tableCell(); if (getExcludedPages() == null || getExcludedPages().length == 0) { getSink().text(i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.summary.table.none")); } else { getSink().text(StringUtils.join(getExcludedPages(), ",")); } getSink().tableCell_(); getSink().tableRow_(); getSink().tableRow(); getSink().tableCell(); getSink().rawText(i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.summary.table.excludedLinks")); getSink().tableCell_(); getSink().tableCell(); if (excludedLinks == null || excludedLinks.length == 0) { getSink().text(i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.summary.table.none")); } else { getSink().text(StringUtils.join(excludedLinks, ",")); } getSink().tableCell_(); getSink().tableRow_(); getSink().tableRow(); getSink().tableCell(); getSink().rawText(i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.summary.table.excludedHttpStatusErrors")); getSink().tableCell_(); getSink().tableCell(); if (excludedHttpStatusErrors == null || excludedHttpStatusErrors.length == 0) { getSink().text(i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.summary.table.none")); } else { getSink().text(toString(excludedHttpStatusErrors)); } getSink().tableCell_(); getSink().tableRow_(); getSink().tableRow(); getSink().tableCell(); getSink().rawText(i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.summary.table.excludedHttpStatusWarnings")); getSink().tableCell_(); getSink().tableCell(); if (excludedHttpStatusWarnings == null || excludedHttpStatusWarnings.length == 0) { getSink().text(i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.summary.table.none")); } else { getSink().text(toString(excludedHttpStatusWarnings)); } getSink().tableCell_(); getSink().tableRow_(); getSink().table_(); // Summary of the checked files getSink().paragraph(); getSink().rawText( i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.summary.overview2")); getSink().paragraph_(); getSink().table(); // Header generateTableHeader(locale, false); // Content getSink().tableRow(); getSink().tableCell(); getSink().bold(); getSink().text(totalFiles + ""); getSink().bold_(); getSink().tableCell_(); getSink().tableCell(); getSink().bold(); getSink().text(totalLinks + ""); getSink().bold_(); getSink().tableCell_(); getSink().tableCell(); getSink().bold(); getSink().text(String.valueOf(totalValidLinks)); getSink().bold_(); getSink().tableCell_(); getSink().tableCell(); getSink().bold(); getSink().text(String.valueOf(totalWarningLinks)); getSink().bold_(); getSink().tableCell_(); getSink().tableCell(); getSink().bold(); getSink().text(String.valueOf(totalErrorLinks)); getSink().bold_(); getSink().tableCell_(); getSink().tableRow_(); getSink().table_(); getSink().section1_(); } private void generateDetailsSection(Locale locale, LinkcheckModel linkcheckModel) { getSink().section1(); getSink().sectionTitle1(); getSink().text(i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.detail")); getSink().sectionTitle1_(); getSink().paragraph(); getSink().rawText( i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.detail.overview")); getSink().paragraph_(); getSink().table(); // Header generateTableHeader(locale, true); // Content List linkcheckFiles = linkcheckModel.getFiles(); for (Iterator it = linkcheckFiles.iterator(); it.hasNext();) { LinkcheckFile linkcheckFile = (LinkcheckFile) it.next(); getSink().tableRow(); getSink().tableCell(); if (linkcheckFile.getUnsuccessful() == 0) { iconValid(locale); } else { iconError(locale); } getSink().tableCell_(); // tableCell( createLinkPatternedText( linkcheckFile.getRelativePath(), "./" // + linkcheckFile.getRelativePath() ) ); getSink().tableCell(); getSink().link(linkcheckFile.getRelativePath()); getSink().text(linkcheckFile.getRelativePath()); getSink().link_(); getSink().tableCell_(); getSink().tableCell(); getSink().text(String.valueOf(linkcheckFile.getNumberOfLinks())); getSink().tableCell_(); getSink().tableCell(); getSink().text(String.valueOf(linkcheckFile.getNumberOfLinks(LinkcheckFileResult.VALID_LEVEL))); getSink().tableCell_(); getSink().tableCell(); getSink().text(String.valueOf(linkcheckFile.getNumberOfLinks(LinkcheckFileResult.WARNING_LEVEL))); getSink().tableCell_(); getSink().tableCell(); getSink().text(String.valueOf(linkcheckFile.getNumberOfLinks(LinkcheckFileResult.ERROR_LEVEL))); getSink().tableCell_(); getSink().tableRow_(); // Detail error if (linkcheckFile.getUnsuccessful() != 0) { getSink().tableRow(); getSink().tableCell(); getSink().text(""); getSink().tableCell_(); // TODO it is due to DOXIA-78 getSink().rawText("<td colspan=\"5\">"); getSink().table(); for (Iterator it2 = linkcheckFile.getResults().iterator(); it2.hasNext();) { LinkcheckFileResult linkcheckFileResult = (LinkcheckFileResult) it2.next(); if (linkcheckFileResult.getStatusLevel() == LinkcheckFileResult.VALID_LEVEL) { continue; } getSink().tableRow(); getSink().tableCell(); if (linkcheckFileResult.getStatusLevel() == LinkcheckFileResult.WARNING_LEVEL) { iconWarning(locale); } else if (linkcheckFileResult.getStatusLevel() == LinkcheckFileResult.ERROR_LEVEL) { iconError(locale); } getSink().tableCell_(); getSink().tableCell(); getSink().italic(); if (linkcheckFileResult.getTarget().startsWith("#")) { getSink().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; } getSink().link(normalizedPath); } else { getSink().link(linkcheckFileResult.getTarget()); } // Show the link as it was written to make it easy for // the author to find it in the source document getSink().text(linkcheckFileResult.getTarget()); getSink().link_(); getSink().text(": "); getSink().text(linkcheckFileResult.getErrorMessage()); getSink().italic_(); getSink().tableCell_(); getSink().tableRow_(); } getSink().table_(); getSink().tableCell_(); getSink().tableRow_(); } } getSink().table_(); getSink().section1_(); } private void generateTableHeader(Locale locale, boolean detail) { getSink().tableRow(); if (detail) { getSink().rawText("<th rowspan=\"2\">"); getSink().text(""); getSink().tableHeaderCell_(); } getSink().rawText("<th rowspan=\"2\">"); getSink().text(detail ? i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.detail.table.documents") : i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.summary.table.documents")); getSink().tableHeaderCell_(); // TODO it is due to DOXIA-78 getSink().rawText("<th colspan=\"4\" align=\"center\">"); getSink().text(i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.table.links")); getSink().tableHeaderCell_(); getSink().tableRow_(); getSink().tableRow(); getSink().tableHeaderCell(); getSink().text( i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.table.totalLinks")); getSink().tableHeaderCell_(); getSink().tableHeaderCell(); iconValid(locale); getSink().tableHeaderCell_(); getSink().tableHeaderCell(); iconWarning(locale); getSink().tableHeaderCell_(); getSink().tableHeaderCell(); iconError(locale); getSink().tableHeaderCell_(); getSink().tableRow_(); } private void iconError(Locale locale) { getSink().figure(); getSink().figureCaption(); getSink().text(i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.icon.error")); getSink().figureCaption_(); // should be defined in skins getSink().figureGraphics("images/icon_error_sml.gif"); getSink().figure_(); } private void iconValid(Locale locale) { getSink().figure(); getSink().figureCaption(); getSink().text(i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.icon.valid")); getSink().figureCaption_(); // should be defined in skins getSink().figureGraphics("images/icon_success_sml.gif"); getSink().figure_(); } private void iconWarning(Locale locale) { getSink().figure(); getSink().figureCaption(); getSink().text(i18n.getString("sitegen-linkcheck-report", locale, "report.sitegen.linkcheck.icon.warning")); getSink().figureCaption_(); // should be defined in skins getSink().figureGraphics("images/icon_warning_sml.gif"); getSink().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(int[] a) { if (a == null || a.length == 0) { return ""; } StringBuffer buf = new StringBuffer(); buf.append(a[0]); for (int i = 1; i < a.length; i++) { buf.append(", "); buf.append(a[i]); } return buf.toString(); } }