Author: brett
Date: Tue Jul 19 17:08:27 2005
New Revision: 219820
URL: http://svn.apache.org/viewcvs?rev=219820&view=rev
Log:
Submitted by: Vincent Siveton
Reviewed by: Brett Porter
reporting fixes
Modified:
maven/components/trunk/maven-reporting/maven-reporting-api/pom.xml
maven/components/trunk/maven-reporting/maven-reporting-api/src/main/java/org/apache/maven/reporting/AbstractMavenReportRenderer.java
Modified: maven/components/trunk/maven-reporting/maven-reporting-api/pom.xml
URL:
http://svn.apache.org/viewcvs/maven/components/trunk/maven-reporting/maven-reporting-api/pom.xml?rev=219820&r1=219819&r2=219820&view=diff
==============================================================================
--- maven/components/trunk/maven-reporting/maven-reporting-api/pom.xml
(original)
+++ maven/components/trunk/maven-reporting/maven-reporting-api/pom.xml Tue Jul
19 17:08:27 2005
@@ -1,42 +1,67 @@
-<project>
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <artifactId>maven-reporting</artifactId>
- <groupId>org.apache.maven.reporting</groupId>
- <version>2.0-beta-1-SNAPSHOT</version>
- </parent>
- <artifactId>maven-reporting-api</artifactId>
- <contributors>
- <contributor>
- <name>Vincent Siveton</name>
- <email>[EMAIL PROTECTED]</email>
- </contributor>
- </contributors>
- <dependencies>
- <dependency>
- <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>
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<!--
+/*
+ * Copyright 2001-2005 The Apache Software Foundation.
+ *
+ * Licensed 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.
+ */
+ -->
+
+<project>
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>maven-reporting</artifactId>
+ <groupId>org.apache.maven.reporting</groupId>
+ <version>2.0-beta-1-SNAPSHOT</version>
+ </parent>
+ <artifactId>maven-reporting-api</artifactId>
+ <developers>
+ <developer>
+ <id>vsiveton</id>
+ <name>Vincent Siveton</name>
+ <email>[EMAIL PROTECTED]</email>
+ <roles>
+ <role>Java Developer</role>
+ </roles>
+ <timezone>-5</timezone>
+ </developer>
+ </developers>
+ <dependencies>
+ <dependency>
+ <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>
Modified:
maven/components/trunk/maven-reporting/maven-reporting-api/src/main/java/org/apache/maven/reporting/AbstractMavenReportRenderer.java
URL:
http://svn.apache.org/viewcvs/maven/components/trunk/maven-reporting/maven-reporting-api/src/main/java/org/apache/maven/reporting/AbstractMavenReportRenderer.java?rev=219820&r1=219819&r2=219820&view=diff
==============================================================================
---
maven/components/trunk/maven-reporting/maven-reporting-api/src/main/java/org/apache/maven/reporting/AbstractMavenReportRenderer.java
(original)
+++
maven/components/trunk/maven-reporting/maven-reporting-api/src/main/java/org/apache/maven/reporting/AbstractMavenReportRenderer.java
Tue Jul 19 17:08:27 2005
@@ -1,315 +1,592 @@
-package org.apache.maven.reporting;
-
-/*
- * Copyright 2004-2005 The Apache Software Foundation.
- *
- * Licensed 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.validator.EmailValidator;
-import org.apache.commons.validator.UrlValidator;
-import org.codehaus.doxia.sink.Sink;
-
-/**
- * @author <a href="mailto:[EMAIL PROTECTED]">Jason van Zyl</a>
- * @author <a href="[EMAIL PROTECTED]">Emmanuel Venisse</a>
- * @author <a href="mailto:[EMAIL PROTECTED]">Vincent Siveton</a>
- * @version $Id: AbstractMavenReportRenderer.java 163373 2005-02-22 03:37:00Z
brett $
- * @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.
- */
-public abstract class AbstractMavenReportRenderer
- implements MavenReportRenderer
-{
- protected Sink sink;
-
- private int section = 0;
-
- public AbstractMavenReportRenderer( Sink sink )
- {
- this.sink = sink;
- }
-
- public void render()
- {
- sink.head();
-
- sink.title();
-
- sink.text( getTitle() );
-
- sink.title_();
-
- sink.head_();
-
- sink.body();
-
- renderBody();
-
- sink.body_();
-
- sink.flush();
-
- sink.close();
- }
-
- protected void startTable()
- {
- sink.table();
- }
-
- protected void endTable()
- {
- sink.table_();
- }
-
- protected void startSection( String name )
- {
- section = section + 1;
-
- switch ( section )
- {
- case 1:
- sink.section1();
- sink.sectionTitle1();
- break;
- case 2:
- sink.section2();
- sink.sectionTitle2();
- break;
- case 3:
- sink.section3();
- sink.sectionTitle3();
- break;
- case 4:
- sink.section4();
- sink.sectionTitle4();
- break;
- case 5:
- sink.section5();
- sink.sectionTitle5();
- break;
-
- default:
- // TODO: warning - just don't start a section
- break;
- }
-
- sink.text( name );
-
- switch ( section )
- {
- case 1:
- sink.sectionTitle1_();
- break;
- case 2:
- sink.sectionTitle2_();
- break;
- case 3:
- sink.sectionTitle3_();
- break;
- case 4:
- sink.sectionTitle4_();
- break;
- case 5:
- sink.sectionTitle5_();
- break;
-
- default:
- // TODO: warning - just don't start a section
- break;
- }
- }
-
- protected void endSection()
- {
- switch ( section )
- {
- case 1:
- sink.section1_();
- break;
- case 2:
- sink.section2_();
- break;
- case 3:
- sink.section3_();
- break;
- case 4:
- sink.section4_();
- break;
- case 5:
- sink.section5_();
- break;
-
- default:
- // TODO: warning - just don't start a section
- break;
- }
-
- section = section - 1;
-
- if ( section < 0 )
- {
- throw new IllegalStateException( "Too many closing sections" );
- }
- }
-
- protected void tableHeaderCell( String text )
- {
- sink.tableHeaderCell();
-
- sink.text( text );
-
- sink.tableHeaderCell_();
- }
-
- protected void tableCell( String text )
- {
- sink.tableCell();
-
- if ( text != null )
- {
- sink.text( text );
- }
- else
- {
- sink.nonBreakingSpace();
- }
-
- sink.tableCell_();
- }
-
- /**
- * Create a cell with a potential link.
- *
- * @param text the text
- * @param href the href
- */
- protected void tableCellWithLink( String text, String href )
- {
- sink.tableCell();
-
- if ( text != null )
- {
- if ( href != null )
- {
- String[] schemes = {"http", "https"};
- UrlValidator urlValidator = new UrlValidator( schemes );
-
- if ( EmailValidator.getInstance().isValid( href ) )
- {
- link( "mailto:" + href, text );
- }
- else if ( href.toLowerCase().startsWith( "mailto:" ) )
- {
- link( href, text );
- }
- else if ( urlValidator.isValid( href ) )
- {
- link( href, text );
- }
- else
- {
- sink.text( text );
- }
- }
- else
- {
- sink.text( text );
- }
- }
- else
- {
- sink.nonBreakingSpace();
- }
-
- sink.tableCell_();
- }
-
- protected void tableRow( String[] content )
- {
- sink.tableRow();
-
- for ( int i = 0; i < content.length; i++ )
- {
- tableCell( content[i] );
- }
-
- sink.tableRow_();
- }
-
- /**
- * Create a new row : each cell could have a link.
- * <br>
- * The arrays should have the same size.
- *
- * @param texts an array of text
- * @param hrefs an array of href
- */
- protected void tableRowWithLink( String[] texts, String[] hrefs )
- {
- if ( hrefs.length != texts.length )
- {
- throw new IllegalArgumentException( "The arrays should have the
same size" );
- }
-
- sink.tableRow();
-
- for ( int i = 0; i < texts.length; i++ )
- {
- tableCellWithLink( texts[i], hrefs[i] );
- }
-
- sink.tableRow_();
- }
-
- protected void tableHeader( String[] content )
- {
- sink.tableRow();
-
- for ( int i = 0; i < content.length; i++ )
- {
- tableHeaderCell( content[i] );
- }
-
- sink.tableRow_();
- }
-
- protected void tableCaption( String caption )
- {
- sink.tableCaption();
- sink.text( caption );
- sink.tableCaption_();
- }
-
- protected void paragraph( String paragraph )
- {
- sink.paragraph();
-
- sink.text( paragraph );
-
- sink.paragraph_();
- }
-
- protected void link( String href, String name )
- {
- sink.link( href );
-
- sink.text( name );
-
- sink.link_();
- }
-
- public abstract String getTitle();
-
- protected abstract void renderBody();
-}
+package org.apache.maven.reporting;
+
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ *
+ * Licensed 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.validator.EmailValidator;
+import org.apache.commons.validator.UrlValidator;
+import org.codehaus.doxia.sink.Sink;
+import org.codehaus.plexus.util.StringUtils;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * An abstract class to manage report generation.
+ *
+ * @author <a href="mailto:[EMAIL PROTECTED]">Jason van Zyl</a>
+ * @author <a href="[EMAIL PROTECTED]">Emmanuel Venisse</a>
+ * @author <a href="mailto:[EMAIL PROTECTED]">Vincent Siveton</a>
+ * @version $Id: AbstractMavenReportRenderer.java 163373 2005-02-22 03:37:00Z
brett $
+ * @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.
+ */
+public abstract class AbstractMavenReportRenderer
+ implements MavenReportRenderer
+{
+ protected Sink sink;
+
+ private int section;
+
+ public AbstractMavenReportRenderer( Sink sink )
+ {
+ this.sink = sink;
+ }
+
+ public void render()
+ {
+ sink.head();
+
+ sink.title();
+
+ text( getTitle() );
+
+ sink.title_();
+
+ sink.head_();
+
+ sink.body();
+
+ renderBody();
+
+ sink.body_();
+
+ sink.flush();
+
+ sink.close();
+ }
+
+ protected void startTable()
+ {
+ sink.table();
+ }
+
+ protected void endTable()
+ {
+ sink.table_();
+ }
+
+ protected void startSection( String name )
+ {
+ section = section + 1;
+
+ switch ( section )
+ {
+ case 1:
+ sink.section1();
+ sink.sectionTitle1();
+ break;
+ case 2:
+ sink.section2();
+ sink.sectionTitle2();
+ break;
+ case 3:
+ sink.section3();
+ sink.sectionTitle3();
+ break;
+ case 4:
+ sink.section4();
+ sink.sectionTitle4();
+ break;
+ case 5:
+ sink.section5();
+ sink.sectionTitle5();
+ break;
+
+ default:
+ // TODO: warning - just don't start a section
+ break;
+ }
+
+ text( name );
+
+ switch ( section )
+ {
+ case 1:
+ sink.sectionTitle1_();
+ break;
+ case 2:
+ sink.sectionTitle2_();
+ break;
+ case 3:
+ sink.sectionTitle3_();
+ break;
+ case 4:
+ sink.sectionTitle4_();
+ break;
+ case 5:
+ sink.sectionTitle5_();
+ break;
+
+ default:
+ // TODO: warning - just don't start a section
+ break;
+ }
+ }
+
+ protected void endSection()
+ {
+ switch ( section )
+ {
+ case 1:
+ sink.section1_();
+ break;
+ case 2:
+ sink.section2_();
+ break;
+ case 3:
+ sink.section3_();
+ break;
+ case 4:
+ sink.section4_();
+ break;
+ case 5:
+ sink.section5_();
+ break;
+
+ default:
+ // TODO: warning - just don't start a section
+ break;
+ }
+
+ section = section - 1;
+
+ if ( section < 0 )
+ {
+ throw new IllegalStateException( "Too many closing sections" );
+ }
+ }
+
+ protected void tableHeaderCell( String text )
+ {
+ sink.tableHeaderCell();
+
+ text( text );
+
+ sink.tableHeaderCell_();
+ }
+
+ /**
+ * Add a cell in a table.
+ * <p>The text could be a link patterned text defined by <code>{text,
url}</code></p>
+ *
+ * @param text
+ * @see #linkPatternedText(String)
+ */
+ protected void tableCell( String text )
+ {
+ sink.tableCell();
+
+ linkPatternedText( text );
+
+ sink.tableCell_();
+ }
+
+ protected void tableRow( String[] content )
+ {
+ sink.tableRow();
+
+ for ( int i = 0; i < content.length; i++ )
+ {
+ tableCell( content[i] );
+ }
+
+ sink.tableRow_();
+ }
+
+ protected void tableHeader( String[] content )
+ {
+ sink.tableRow();
+
+ for ( int i = 0; i < content.length; i++ )
+ {
+ tableHeaderCell( content[i] );
+ }
+
+ sink.tableRow_();
+ }
+
+ protected void tableCaption( String caption )
+ {
+ sink.tableCaption();
+ text( caption );
+ sink.tableCaption_();
+ }
+
+ protected void paragraph( String paragraph )
+ {
+ sink.paragraph();
+
+ text( paragraph );
+
+ sink.paragraph_();
+ }
+
+ protected void link( String href, String name )
+ {
+ sink.link( href );
+
+ text( name );
+
+ sink.link_();
+ }
+
+ /**
+ * Add a new text.
+ * <p>If text is empty of has a null value, add the "-" charater</p>
+ *
+ * @param text a string
+ */
+ protected void text( String text )
+ {
+ if ( text == null || text.length() == 0 ) // Take care of spaces
+ {
+ sink.text( "-" );
+ }
+ else
+ {
+ sink.text( text );
+ }
+ }
+
+ /**
+ * Add a verbatim text.
+ *
+ * @param text a string
+ * @see #text(String)
+ */
+ protected void verbatimText( String text )
+ {
+ sink.verbatim( true );
+
+ text( text );
+
+ sink.verbatim_();
+ }
+
+ /**
+ * Add a verbatim text with a specific link.
+ *
+ * @param text a string
+ * @param href an href could be null
+ * @see #link(String, String)
+ */
+ protected void verbatimLink( String text, String href )
+ {
+ if ( StringUtils.isEmpty( href ) )
+ {
+ verbatimText( text );
+ }
+ else
+ {
+ sink.verbatim( true );
+
+ link( href, text );
+
+ sink.verbatim_();
+ }
+ }
+
+ /**
+ * Add a Javascript code.
+ *
+ * @param jsCode a string of Javascript
+ */
+ protected void javaScript( String jsCode )
+ {
+ sink.rawText( "<script type=\"text/javascript\">\n" + jsCode +
"</script>" );
+ }
+
+ /**
+ * 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();
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]