diff --git a/maven-plugin-tools-api/src/main/java/org/apache/maven/tools/plugin/generator/PluginXdocGenerator.java b/maven-plugin-tools-api/src/main/java/org/apache/maven/tools/plugin/generator/PluginXdocGenerator.java index e619acc..bc3cac8 100644 --- a/maven-plugin-tools-api/src/main/java/org/apache/maven/tools/plugin/generator/PluginXdocGenerator.java +++ b/maven-plugin-tools-api/src/main/java/org/apache/maven/tools/plugin/generator/PluginXdocGenerator.java @@ -25,6 +25,8 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.apache.maven.plugin.descriptor.MojoDescriptor; import org.apache.maven.plugin.descriptor.Parameter; @@ -489,7 +491,7 @@ public class PluginXdocGenerator tidy.setMakeClean( true ); tidy.setQuiet( true ); tidy.setShowWarnings( false ); - tidy.parse( new StringInputStream( description ), out ); + tidy.parse( new StringInputStream( decodeJavadocTags( description ) ), out ); // strip the header/body stuff String LS = System.getProperty( "line.separator" ); @@ -503,4 +505,116 @@ public class PluginXdocGenerator return commentCleaned.substring( startPos, endPos ); } + + /** + * Decodes javadoc inline tags into equivalent HTML tags. For instance, the inline tag "{@code }" should be + * rendered as "<A&B>". + * + * @param description The javadoc description to decode, may be null. + * @return The decoded description, never null. + */ + protected static String decodeJavadocTags( String description ) + { + if ( StringUtils.isEmpty( description ) ) + { + return ""; + } + + StringBuffer decoded = new StringBuffer( description.length() + 1024 ); + + Matcher matcher = Pattern.compile( "\\{@(\\w+)\\s*([^\\}]*)\\}" ).matcher( description ); + while ( matcher.find() ) + { + String tag = matcher.group( 1 ); + String text = matcher.group( 2 ); + text = StringUtils.replace( text, "&", "&" ); + text = StringUtils.replace( text, "<", "<" ); + text = StringUtils.replace( text, ">", ">" ); + if ( "code".equals( tag ) ) + { + text = "" + text + ""; + } + else if ( "link".equals( tag ) || "linkplain".equals( tag ) || "value".equals( tag ) ) + { + String pattern = "(([^#\\.\\s]+\\.)*([^#\\.\\s]+))?" + "(#([^\\(\\s]*)(\\([^\\)]*\\))?\\s*(\\S.*)?)?"; + final int LABEL = 7; + final int CLASS = 3; + final int MEMBER = 5; + final int ARGS = 6; + Matcher link = Pattern.compile( pattern ).matcher( text ); + if ( link.matches() ) + { + text = link.group( LABEL ); + if ( StringUtils.isEmpty( text ) ) + { + text = link.group( CLASS ); + if ( StringUtils.isEmpty( text ) ) + { + text = ""; + } + if ( StringUtils.isNotEmpty( link.group( MEMBER ) ) ) + { + if ( StringUtils.isNotEmpty( text ) ) + { + text += '.'; + } + text += link.group( MEMBER ); + if ( StringUtils.isNotEmpty( link.group( ARGS ) ) ) + { + text += "()"; + } + } + } + } + if ( !"linkplain".equals( tag ) ) + { + text = "" + text + ""; + } + } + matcher.appendReplacement( decoded, ( text != null ) ? quoteReplacement( text ) : "" ); + } + matcher.appendTail( decoded ); + + return decoded.toString(); + } + + /** + * Returns a literal replacement String for the specified String. This method + * produces a String that will work as a literal replacement s in the + * appendReplacement method of the {@link Matcher} class. The String produced will + * match the sequence of characters in s treated as a literal sequence. Slashes ('\') and dollar + * signs ('$') will be given no special meaning. + * + * TODO: copied from Matcher class of Java 1.5, remove once target platform can be upgraded + * @see + * http://java.sun.com/j2se/1.5.0/docs/api/java/util/regex/Matcher.html + * + * @param s The string to be literalized + * @return A literal string replacement + */ + private static String quoteReplacement( String s ) + { + if ( ( s.indexOf( '\\' ) == -1 ) && ( s.indexOf( '$' ) == -1 ) ) + return s; + StringBuffer sb = new StringBuffer(); + for ( int i = 0; i < s.length(); i++ ) + { + char c = s.charAt( i ); + if ( c == '\\' ) + { + sb.append( '\\' ); + sb.append( '\\' ); + } + else if ( c == '$' ) + { + sb.append( '\\' ); + sb.append( '$' ); + } + else + { + sb.append( c ); + } + } + return sb.toString(); + } } diff --git a/maven-plugin-tools-api/src/test/java/org/apache/maven/tools/plugin/generator/PluginXdocGeneratorTest.java b/maven-plugin-tools-api/src/test/java/org/apache/maven/tools/plugin/generator/PluginXdocGeneratorTest.java index 41f6b5c..3c4c99f 100644 --- a/maven-plugin-tools-api/src/test/java/org/apache/maven/tools/plugin/generator/PluginXdocGeneratorTest.java +++ b/maven-plugin-tools-api/src/test/java/org/apache/maven/tools/plugin/generator/PluginXdocGeneratorTest.java @@ -41,4 +41,58 @@ public class PluginXdocGeneratorTest assertEquals( "Generates something for the project.", PluginXdocGenerator .makeHtmlValid( javadoc ) ); } -} \ No newline at end of file + + public void testDecodeJavadocTags() + { + String javadoc = null; + assertEquals( "", PluginXdocGenerator.decodeJavadocTags( javadoc ) ); + + javadoc = ""; + assertEquals( "", PluginXdocGenerator.decodeJavadocTags( javadoc ) ); + + javadoc = "{@code text}"; + assertEquals( "text", PluginXdocGenerator.decodeJavadocTags( javadoc ) ); + + javadoc = "{@code }"; + assertEquals( "<A&B>", PluginXdocGenerator.decodeJavadocTags( javadoc ) ); + + javadoc = "{@literal text}"; + assertEquals( "text", PluginXdocGenerator.decodeJavadocTags( javadoc ) ); + + javadoc = "{@literal text} {@literal text}"; + assertEquals( "text text", PluginXdocGenerator.decodeJavadocTags( javadoc ) ); + + javadoc = "{@literal }"; + assertEquals( "<A&B>", PluginXdocGenerator.decodeJavadocTags( javadoc ) ); + + javadoc = "{@link Class}"; + assertEquals( "Class", PluginXdocGenerator.decodeJavadocTags( javadoc ) ); + + javadoc = "{@linkplain Class}"; + assertEquals( "Class", PluginXdocGenerator.decodeJavadocTags( javadoc ) ); + + javadoc = "{@linkplain #field}"; + assertEquals( "field", PluginXdocGenerator.decodeJavadocTags( javadoc ) ); + + javadoc = "{@linkplain Class#field}"; + assertEquals( "Class.field", PluginXdocGenerator.decodeJavadocTags( javadoc ) ); + + javadoc = "{@linkplain #method()}"; + assertEquals( "method()", PluginXdocGenerator.decodeJavadocTags( javadoc ) ); + + javadoc = "{@linkplain #method(Object arg)}"; + assertEquals( "method()", PluginXdocGenerator.decodeJavadocTags( javadoc ) ); + + javadoc = "{@linkplain #method(Object, String)}"; + assertEquals( "method()", PluginXdocGenerator.decodeJavadocTags( javadoc ) ); + + javadoc = "{@linkplain #method(Object, String) label}"; + assertEquals( "label", PluginXdocGenerator.decodeJavadocTags( javadoc ) ); + + javadoc = "{@linkplain Class#method(Object, String)}"; + assertEquals( "Class.method()", PluginXdocGenerator.decodeJavadocTags( javadoc ) ); + + javadoc = "{@linkplain Class#method(Object, String) label}"; + assertEquals( "label", PluginXdocGenerator.decodeJavadocTags( javadoc ) ); + } +}