Submitted by: Vincent Siveton

Reviewed by:  Brett Porter
reporting fixes

git-svn-id: https://svn.apache.org/repos/asf/maven/components/trunk@219820 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Brett Leslie Porter 2005-07-20 00:08:27 +00:00
parent 19696518b3
commit 694bcf59ab
2 changed files with 659 additions and 357 deletions

View File

@ -1,42 +1,67 @@
<project> <?xml version="1.0" encoding="ISO-8859-1"?>
<modelVersion>4.0.0</modelVersion>
<parent> <!--
<artifactId>maven-reporting</artifactId> /*
<groupId>org.apache.maven.reporting</groupId> * Copyright 2001-2005 The Apache Software Foundation.
<version>2.0-beta-1-SNAPSHOT</version> *
</parent> * Licensed under the Apache License, Version 2.0 (the "License");
<artifactId>maven-reporting-api</artifactId> * you may not use this file except in compliance with the License.
<contributors> * You may obtain a copy of the License at
<contributor> *
<name>Vincent Siveton</name> * http://www.apache.org/licenses/LICENSE-2.0
<email>vincent.siveton@gmail.com</email> *
</contributor> * Unless required by applicable law or agreed to in writing, software
</contributors> * distributed under the License is distributed on an "AS IS" BASIS,
<dependencies> * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
<dependency> * See the License for the specific language governing permissions and
<groupId>org.apache.maven</groupId> * limitations under the License.
<artifactId>maven-project</artifactId> */
<version>2.0-beta-1-SNAPSHOT</version> -->
</dependency>
<dependency> <project>
<groupId>doxia</groupId> <modelVersion>4.0.0</modelVersion>
<artifactId>doxia-core</artifactId> <parent>
<version>1.0-alpha-4-SNAPSHOT</version> <artifactId>maven-reporting</artifactId>
</dependency> <groupId>org.apache.maven.reporting</groupId>
<dependency> <version>2.0-beta-1-SNAPSHOT</version>
<groupId>org.apache.maven</groupId> </parent>
<artifactId>maven-plugin-api</artifactId> <artifactId>maven-reporting-api</artifactId>
<version>2.0-beta-1-SNAPSHOT</version> <developers>
</dependency> <developer>
<dependency> <id>vsiveton</id>
<groupId>commons-validator</groupId> <name>Vincent Siveton</name>
<artifactId>commons-validator</artifactId> <email>vincent.siveton@gmail.com</email>
<version>1.1.4</version> <roles>
</dependency> <role>Java Developer</role>
<dependency> </roles>
<groupId>oro</groupId> <timezone>-5</timezone>
<artifactId>oro</artifactId> </developer>
<version>2.0.7</version> </developers>
</dependency> <dependencies>
</dependencies> <dependency>
</project> <groupId>org.apache.maven</groupId>
<artifactId>maven-project</artifactId>
<version>2.0-beta-1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>doxia</groupId>
<artifactId>doxia-core</artifactId>
<version>1.0-alpha-4-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>2.0-beta-1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
<version>1.1.4</version>
</dependency>
<dependency>
<groupId>oro</groupId>
<artifactId>oro</artifactId>
<version>2.0.7</version>
</dependency>
</dependencies>
</project>

View File

@ -1,315 +1,592 @@
package org.apache.maven.reporting; package org.apache.maven.reporting;
/* /*
* Copyright 2004-2005 The Apache Software Foundation. * Copyright 2004-2005 The Apache Software Foundation.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import org.apache.commons.validator.EmailValidator; import org.apache.commons.validator.EmailValidator;
import org.apache.commons.validator.UrlValidator; import org.apache.commons.validator.UrlValidator;
import org.codehaus.doxia.sink.Sink; import org.codehaus.doxia.sink.Sink;
import org.codehaus.plexus.util.StringUtils;
/**
* @author <a href="mailto:jason@maven.org">Jason van Zyl</a> import java.util.Collections;
* @author <a href="evenisse@apache.org">Emmanuel Venisse</a> import java.util.Iterator;
* @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a> import java.util.LinkedHashMap;
* @version $Id: AbstractMavenReportRenderer.java 163373 2005-02-22 03:37:00Z brett $ import java.util.Map;
* @todo Later it may be appropriate to create something like a VelocityMavenReportRenderer that could take a velocity template and pipe that through Doxia rather than coding them up like this. import java.util.Properties;
*/
public abstract class AbstractMavenReportRenderer /**
implements MavenReportRenderer * An abstract class to manage report generation.
{ *
protected Sink sink; * @author <a href="mailto:jason@maven.org">Jason van Zyl</a>
* @author <a href="evenisse@apache.org">Emmanuel Venisse</a>
private int section = 0; * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
* @version $Id: AbstractMavenReportRenderer.java 163373 2005-02-22 03:37:00Z brett $
public AbstractMavenReportRenderer( Sink sink ) * @todo Later it may be appropriate to create something like a VelocityMavenReportRenderer that could take a velocity template and pipe that through Doxia rather than coding them up like this.
{ */
this.sink = sink; public abstract class AbstractMavenReportRenderer
} implements MavenReportRenderer
{
public void render() protected Sink sink;
{
sink.head(); private int section;
sink.title(); public AbstractMavenReportRenderer( Sink sink )
{
sink.text( getTitle() ); this.sink = sink;
}
sink.title_();
public void render()
sink.head_(); {
sink.head();
sink.body();
sink.title();
renderBody();
text( getTitle() );
sink.body_();
sink.title_();
sink.flush();
sink.head_();
sink.close();
} sink.body();
protected void startTable() renderBody();
{
sink.table(); sink.body_();
}
sink.flush();
protected void endTable()
{ sink.close();
sink.table_(); }
}
protected void startTable()
protected void startSection( String name ) {
{ sink.table();
section = section + 1; }
switch ( section ) protected void endTable()
{ {
case 1: sink.table_();
sink.section1(); }
sink.sectionTitle1();
break; protected void startSection( String name )
case 2: {
sink.section2(); section = section + 1;
sink.sectionTitle2();
break; switch ( section )
case 3: {
sink.section3(); case 1:
sink.sectionTitle3(); sink.section1();
break; sink.sectionTitle1();
case 4: break;
sink.section4(); case 2:
sink.sectionTitle4(); sink.section2();
break; sink.sectionTitle2();
case 5: break;
sink.section5(); case 3:
sink.sectionTitle5(); sink.section3();
break; sink.sectionTitle3();
break;
default: case 4:
// TODO: warning - just don't start a section sink.section4();
break; sink.sectionTitle4();
} break;
case 5:
sink.text( name ); sink.section5();
sink.sectionTitle5();
switch ( section ) break;
{
case 1: default:
sink.sectionTitle1_(); // TODO: warning - just don't start a section
break; break;
case 2: }
sink.sectionTitle2_();
break; text( name );
case 3:
sink.sectionTitle3_(); switch ( section )
break; {
case 4: case 1:
sink.sectionTitle4_(); sink.sectionTitle1_();
break; break;
case 5: case 2:
sink.sectionTitle5_(); sink.sectionTitle2_();
break; break;
case 3:
default: sink.sectionTitle3_();
// TODO: warning - just don't start a section break;
break; case 4:
} sink.sectionTitle4_();
} break;
case 5:
protected void endSection() sink.sectionTitle5_();
{ break;
switch ( section )
{ default:
case 1: // TODO: warning - just don't start a section
sink.section1_(); break;
break; }
case 2: }
sink.section2_();
break; protected void endSection()
case 3: {
sink.section3_(); switch ( section )
break; {
case 4: case 1:
sink.section4_(); sink.section1_();
break; break;
case 5: case 2:
sink.section5_(); sink.section2_();
break; break;
case 3:
default: sink.section3_();
// TODO: warning - just don't start a section break;
break; case 4:
} sink.section4_();
break;
section = section - 1; case 5:
sink.section5_();
if ( section < 0 ) break;
{
throw new IllegalStateException( "Too many closing sections" ); default:
} // TODO: warning - just don't start a section
} break;
}
protected void tableHeaderCell( String text )
{ section = section - 1;
sink.tableHeaderCell();
if ( section < 0 )
sink.text( text ); {
throw new IllegalStateException( "Too many closing sections" );
sink.tableHeaderCell_(); }
} }
protected void tableCell( String text ) protected void tableHeaderCell( String text )
{ {
sink.tableCell(); sink.tableHeaderCell();
if ( text != null ) text( text );
{
sink.text( text ); sink.tableHeaderCell_();
} }
else
{ /**
sink.nonBreakingSpace(); * Add a cell in a table.
} * <p>The text could be a link patterned text defined by <code>{text, url}</code></p>
*
sink.tableCell_(); * @param text
} * @see #linkPatternedText(String)
*/
/** protected void tableCell( String text )
* Create a cell with a potential link. {
* sink.tableCell();
* @param text the text
* @param href the href linkPatternedText( text );
*/
protected void tableCellWithLink( String text, String href ) sink.tableCell_();
{ }
sink.tableCell();
protected void tableRow( String[] content )
if ( text != null ) {
{ sink.tableRow();
if ( href != null )
{ for ( int i = 0; i < content.length; i++ )
String[] schemes = {"http", "https"}; {
UrlValidator urlValidator = new UrlValidator( schemes ); tableCell( content[i] );
}
if ( EmailValidator.getInstance().isValid( href ) )
{ sink.tableRow_();
link( "mailto:" + href, text ); }
}
else if ( href.toLowerCase().startsWith( "mailto:" ) ) protected void tableHeader( String[] content )
{ {
link( href, text ); sink.tableRow();
}
else if ( urlValidator.isValid( href ) ) for ( int i = 0; i < content.length; i++ )
{ {
link( href, text ); tableHeaderCell( content[i] );
} }
else
{ sink.tableRow_();
sink.text( text ); }
}
} protected void tableCaption( String caption )
else {
{ sink.tableCaption();
sink.text( text ); text( caption );
} sink.tableCaption_();
} }
else
{ protected void paragraph( String paragraph )
sink.nonBreakingSpace(); {
} sink.paragraph();
sink.tableCell_(); text( paragraph );
}
sink.paragraph_();
protected void tableRow( String[] content ) }
{
sink.tableRow(); protected void link( String href, String name )
{
for ( int i = 0; i < content.length; i++ ) sink.link( href );
{
tableCell( content[i] ); text( name );
}
sink.link_();
sink.tableRow_(); }
}
/**
/** * Add a new text.
* Create a new row : each cell could have a link. * <p>If text is empty of has a null value, add the "-" charater</p>
* <br> *
* The arrays should have the same size. * @param text a string
* */
* @param texts an array of text protected void text( String text )
* @param hrefs an array of href {
*/ if ( text == null || text.length() == 0 ) // Take care of spaces
protected void tableRowWithLink( String[] texts, String[] hrefs ) {
{ sink.text( "-" );
if ( hrefs.length != texts.length ) }
{ else
throw new IllegalArgumentException( "The arrays should have the same size" ); {
} sink.text( text );
}
sink.tableRow(); }
for ( int i = 0; i < texts.length; i++ ) /**
{ * Add a verbatim text.
tableCellWithLink( texts[i], hrefs[i] ); *
} * @param text a string
* @see #text(String)
sink.tableRow_(); */
} protected void verbatimText( String text )
{
protected void tableHeader( String[] content ) sink.verbatim( true );
{
sink.tableRow(); text( text );
for ( int i = 0; i < content.length; i++ ) sink.verbatim_();
{ }
tableHeaderCell( content[i] );
} /**
* Add a verbatim text with a specific link.
sink.tableRow_(); *
} * @param text a string
* @param href an href could be null
protected void tableCaption( String caption ) * @see #link(String, String)
{ */
sink.tableCaption(); protected void verbatimLink( String text, String href )
sink.text( caption ); {
sink.tableCaption_(); if ( StringUtils.isEmpty( href ) )
} {
verbatimText( text );
protected void paragraph( String paragraph ) }
{ else
sink.paragraph(); {
sink.verbatim( true );
sink.text( paragraph );
link( href, text );
sink.paragraph_();
} sink.verbatim_();
}
protected void link( String href, String name ) }
{
sink.link( href ); /**
* Add a Javascript code.
sink.text( name ); *
* @param jsCode a string of Javascript
sink.link_(); */
} protected void javaScript( String jsCode )
{
public abstract String getTitle(); sink.rawText( "<script type=\"text/javascript\">\n" + jsCode + "</script>" );
}
protected abstract void renderBody();
} /**
* Add a text with links inside.
* <p>The text variable should contained this given pattern <code>{text, url}</code>
* to handle the link creation.</p>
*
* @param text a text with link pattern defined.
* @see #text(String)
* @see #applyPattern(String)
*/
public void linkPatternedText( String text )
{
if ( StringUtils.isEmpty( text ) )
{
text( text );
}
else
{
Map segments = applyPattern( text );
if ( segments == null )
{
text( text );
}
else
{
for ( Iterator it = segments.entrySet().iterator(); it.hasNext(); )
{
Map.Entry entry = (Map.Entry) it.next();
String name = (String) entry.getKey();
String href = (String) entry.getValue();
if ( href == null )
{
text( name );
}
else
{
if ( getValidHref( href ) != null )
{
link( getValidHref( href ), name );
}
else
{
text( text );
}
}
}
}
}
}
/**
* Create a link pattern text defined by <code>{text, url}</code>.
* <p>This created pattern could be used by the method <code>linkPatternedText(String)</code> to
* handle a text with link.</p>
*
* @param text
* @param href
* @return a link pattern
* @see #linkPatternedText(String)
*/
protected static String createLinkPatternedText( String text, String href )
{
if ( text == null )
{
return text;
}
if ( href == null )
{
return text;
}
StringBuffer sb = new StringBuffer();
sb.append( "{" ).append( text ).append( ", " ).append( href ).append( "}" );
return sb.toString();
}
/**
* Convenience method to display a <code>Properties</code> object comma separated.
*
* @param props
* @return the properties object as comma separated String
*/
protected static String propertiesToString( Properties props )
{
StringBuffer sb = new StringBuffer();
if ( props == null || props.isEmpty() )
{
return sb.toString();
}
for ( Iterator i = props.keySet().iterator(); i.hasNext(); )
{
String key = (String) i.next();
sb.append( key ).append( "=" ).append( props.get( key ) );
if ( i.hasNext() )
{
sb.append( ", " );
}
}
return sb.toString();
}
/**
* Return a valid href.
* <p>A valid href could start by <code>mailto:</code></p>.
*
* @param href an href
* @return a valid href or null if the href is not valid.
*/
private static String getValidHref( String href )
{
href = href.trim();
String[] schemes = {"http", "https"};
UrlValidator urlValidator = new UrlValidator( schemes );
if ( EmailValidator.getInstance().isValid( href ) )
{
return "mailto:" + href;
}
else if ( href.toLowerCase().startsWith( "mailto:" ) )
{
return href;
}
else if ( urlValidator.isValid( href ) )
{
return href;
}
else
{
// TODO Waiting for new release of Validator
// http://issues.apache.org/bugzilla/show_bug.cgi?id=30686
String hrefTmp;
if ( !href.trim().endsWith( "/" ) )
{
hrefTmp = href + "/index.html";
}
else
{
hrefTmp = href + "index.html";
}
if ( urlValidator.isValid( hrefTmp ) )
{
return href;
}
return null;
}
}
/**
* The method parses a text an apply the given pattern <code>{text, url}</code> to create
* a map of text/href.
*
* @param text a text with or without the pattern <code>{text, url}</code>
* @return a map of text/href
*/
private static Map applyPattern( String text )
{
if ( StringUtils.isEmpty( text ) )
{
return null;
}
// Map defined by key/value name/href
// If href == null, it means
Map segments = new LinkedHashMap();
// TODO Special case http://jira.codehaus.org/browse/MEV-40
if ( text.indexOf( "${" ) != -1 )
{
int lastComma = text.lastIndexOf( "," );
int lastSemi = text.lastIndexOf( "}" );
if ( lastComma != -1 && lastSemi != -1 )
{
segments.put( text.substring( lastComma + 1, lastSemi ).trim(), null );
}
else
{
segments.put( text, null );
}
return segments;
}
boolean inQuote = false;
int braceStack = 0;
int lastOffset = 0;
for ( int i = 0; i < text.length(); i++ )
{
char ch = text.charAt( i );
if ( ch == '\'' && !inQuote )
{
// handle: ''
if ( i + 1 < text.length() && text.charAt( i + 1 ) == '\'' )
{
i++;
}
else
{
inQuote = true;
}
}
else
{
switch ( ch )
{
case '{':
if ( !inQuote )
{
if ( braceStack == 0 )
{
if ( i != 0 ) // handle { at first character
{
segments.put( text.substring( lastOffset, i ), null );
}
lastOffset = i + 1;
braceStack++;
}
}
break;
case '}':
if ( !inQuote )
{
braceStack--;
if ( braceStack == 0 )
{
String subString = text.substring( lastOffset, i );
lastOffset = i + 1;
int lastComma = subString.lastIndexOf( "," );
if ( lastComma != -1 )
{
segments.put( subString.substring( 0, lastComma ).trim(),
subString.substring( lastComma + 1 ).trim() );
}
else
{
segments.put( subString.substring( 0, lastComma ).trim(), null );
}
}
}
break;
case '\'':
inQuote = false;
break;
default:
break;
}
}
}
if ( !StringUtils.isEmpty( text.substring( lastOffset, text.length() ) ) )
{
segments.put( text.substring( lastOffset, text.length() ), null );
}
if ( braceStack != 0 )
{
throw new IllegalArgumentException( "Unmatched braces in the pattern." );
}
return Collections.unmodifiableMap( segments );
}
public abstract String getTitle();
protected abstract void renderBody();
}