blob: 1f6eadc2381da708a89971357c5ddbf1c37eecb0 [file] [log] [blame]
package org.apache.maven.tools.plugin.javadoc;
/*
* 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.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.StringTokenizer;
import javax.swing.text.AttributeSet;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import com.sun.javadoc.Tag;
import com.sun.tools.doclets.Taglet;
/**
* Abstract <code>Taglet</code> for <a href="http://maven.codehaus.org/"/>Maven</a> Mojo annotations.
* <br/>
* A Mojo annotation is defined like the following:
* <pre>
* &#64;annotation &lt;annotationValue&gt; &lt;parameterName="parameterValue"&gt;
* </pre>
*
* @see <a href="package-summary.html#package_description">package-summary.html</a>
*
* @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
* @version $Id$
*/
public abstract class AbstractMojoTaglet
implements Taglet
{
/** {@inheritDoc} */
public String toString( Tag tag )
{
if ( tag == null )
{
return null;
}
String tagValue = getTagValue( tag );
MutableAttributeSet tagAttributes = getTagAttributes( tag );
StringBuilder sb = new StringBuilder();
appendTag( sb, tag, tagAttributes, tagValue );
return sb.toString();
}
/** {@inheritDoc} */
public String toString( Tag[] tags )
{
if ( tags.length == 0 )
{
return null;
}
StringBuilder sb = new StringBuilder();
for ( int i = 0; i < tags.length; i++ )
{
String tagValue = getTagValue( tags[i] );
MutableAttributeSet tagAttributes = getTagAttributes( tags[i] );
appendTag( sb, tags[i], tagAttributes, tagValue );
}
return sb.toString();
}
/**
* @return the header, i.e. the message, to display
*/
public abstract String getHeader();
/**
* @return the given annotation value, or <code>null</code> if the given Mojo annotation/tag does't allow
* annotation value.
* <br/>
* <b>Note</b>: the value could be a pattern value, i.e.: <code>*</code> for every values, <code>a|b|c</code>
* for <code>a OR b OR c</code>.
*/
public abstract String getAllowedValue();
/**
* @return an array of the allowed parameter names for the given Mojo annotation/tag, or <code>null</code>
* if the annotation/tag doesn't allow parameter.
*/
public abstract String[] getAllowedParameterNames();
/**
* @return <code>true</code> if taglet has annotation value, <code>false</code> otherwise.
* @see #getAllowedValue()
*/
public boolean hasAnnotationValue()
{
return getAllowedValue() != null;
}
/**
* @return <code>true</code> if taglet has parameters, <code>false</code> otherwise.
* @see #getAllowedParameterNames()
*/
public boolean hasAnnotationParameters()
{
return getAllowedParameterNames() != null;
}
/**
* @param tag not null.
* @return a not null String or <code>null</code> if no annotation value was found.
*/
private String getTagValue( Tag tag )
{
if ( tag == null )
{
throw new IllegalArgumentException( "tag should be not null" );
}
String text = tag.text();
if ( isEmpty( text ) )
{
// using pattern: @annotation
return null;
}
String tagValue = null;
StringTokenizer token = new StringTokenizer( text, " " );
while ( token.hasMoreTokens() )
{
String nextToken = token.nextToken();
if ( nextToken.indexOf( '=' ) == -1 )
{
// using pattern: @annotation <annotationValue>
tagValue = nextToken;
}
}
return tagValue;
}
/**
* @param tag not null.
* @return a not null MutableAttributeSet.
*/
private MutableAttributeSet getTagAttributes( Tag tag )
{
if ( tag == null )
{
throw new IllegalArgumentException( "tag should be not null" );
}
String text = tag.text();
StringTokenizer token = new StringTokenizer( text, " " );
MutableAttributeSet tagAttributes = new SimpleAttributeSet();
while ( token.hasMoreTokens() )
{
String nextToken = token.nextToken();
if ( nextToken.indexOf( '=' ) == -1 )
{
// using pattern: @annotation <annotationValue>
continue;
}
StringTokenizer token2 = new StringTokenizer( nextToken, "=" );
if ( token2.countTokens() != 2 )
{
System.err.println( "The annotation '" + tag.name() + "' has no name/value pairs parameter: "
+ tag.name() + " " + text + " (" + tag.position().file() + ":" + tag.position().line() + ":"
+ tag.position().column() + ")" );
tagAttributes.addAttribute( token2.nextToken(), "" );
continue;
}
String name = token2.nextToken();
String value = token2.nextToken().replaceAll( "\"", "" );
if ( getAllowedParameterNames() != null && !Arrays.asList( getAllowedParameterNames() ).contains( name ) )
{
System.err.println( "The annotation '" + tag.name() + "' has wrong parameter name: " + tag.name() + " "
+ text + " (" + tag.position().file() + ":" + tag.position().line() + ":" + tag.position().column()
+ ")" );
}
tagAttributes.addAttribute( name, value );
}
return tagAttributes;
}
/**
* Append a tag
*
* @param sb not null
* @param tag not null
* @param tagAttributes not null
* @param tagValue not null
*/
private void appendTag( StringBuilder sb, Tag tag, MutableAttributeSet tagAttributes, String tagValue )
{
if ( !hasAnnotationParameters() )
{
if ( tagAttributes.getAttributeCount() > 0 )
{
System.err.println( "The annotation '@" + getName() + "' should have no attribute ("
+ tag.position().file() + ":" + tag.position().line() + ":" + tag.position().column() + ")" );
}
if ( hasAnnotationValue() )
{
sb.append( "<DT><B>" ).append( getHeader() ).append( ":</B></DT>" );
if ( isEveryValues( getAllowedValue() ) )
{
if ( isNotEmpty( tagValue ) )
{
sb.append( "<DD>" ).append( tagValue ).append( "</DD>" );
}
else
{
System.err.println( "The annotation '@" + getName() + "' is specified to have a value but "
+ "no value is defined (" + tag.position().file() + ":" + tag.position().line() + ":"
+ tag.position().column() + ")" );
sb.append( "<DD>" ).append( "NOT DEFINED" ).append( "</DD>" );
}
}
else
{
List<String> l = getOnlyValues( getAllowedValue() );
if ( isNotEmpty( tagValue ) )
{
if ( l.contains( tagValue ) )
{
sb.append( "<DD>" ).append( tagValue ).append( "</DD>" );
}
else
{
System.err.println( "The annotation '@" + getName() + "' is specified to be a value of "
+ l + " (" + tag.position().file() + ":" + tag.position().line() + ":"
+ tag.position().column() + ")" );
sb.append( "<DD>" ).append( tagValue ).append( "</DD>" );
}
}
else
{
sb.append( "<DD>" ).append( l.get( 0 ) ).append( "</DD>" );
}
}
}
else
{
if ( isNotEmpty( tagValue ) )
{
System.err.println( "The annotation '@" + getName() + "' should have no value ("
+ tag.position().file() + ":" + tag.position().line() + ":" + tag.position().column() + ")" );
}
sb.append( "<DT><B>" ).append( getHeader() ).append( "</B></DT>" );
sb.append( "<DD></DD>" );
}
}
else
{
if ( hasAnnotationValue() )
{
sb.append( "<DT><B>" ).append( getHeader() ).append( ":</B></DT>" );
if ( isEveryValues( getAllowedValue() ) )
{
if ( isNotEmpty( tagValue ) )
{
sb.append( "<DD>" ).append( tagValue );
}
else
{
System.err.println( "The annotation '@" + getName() + "' is specified to have a value but "
+ "no value is defined (" + tag.position().file() + ":" + tag.position().line() + ":"
+ tag.position().column() + ")" );
sb.append( "<DD>" ).append( "NOT DEFINED" );
}
}
else
{
List<String> l = getOnlyValues( getAllowedValue() );
if ( isNotEmpty( tagValue ) )
{
if ( l.contains( tagValue ) )
{
sb.append( "<DD>" ).append( tagValue );
}
else
{
System.err.println( "The annotation '@" + getName() + "' is specified to be a value in "
+ l + " (" + tag.position().file() + ":" + tag.position().line() + ":"
+ tag.position().column() + ")" );
sb.append( "<DD>" ).append( tagValue );
}
}
else
{
sb.append( "<DD>" ).append( l.get( 0 ) );
}
}
}
else
{
if ( isNotEmpty( tagValue ) )
{
System.err.println( "The annotation '@" + getName() + "' should have no value ("
+ tag.position().file() + ":" + tag.position().line() + ":" + tag.position().column() + ")" );
}
sb.append( "<DT><B>" ).append( getHeader() ).append( ":</B></DT>" );
sb.append( "<DD>" );
}
appendAnnotationParameters( sb, tagAttributes );
sb.append( "</DD>" );
}
}
/**
* Append the annotation parameters as a definition list.
*
* @param sb not null
* @param att not null
*/
private static void appendAnnotationParameters( StringBuilder sb, MutableAttributeSet att )
{
sb.append( "<DL>" );
Enumeration<?> names = att.getAttributeNames();
while ( names.hasMoreElements() )
{
Object key = names.nextElement();
Object value = att.getAttribute( key );
if ( value instanceof AttributeSet )
{
// ignored
}
else
{
sb.append( "<DT><B>" ).append( key ).append( ":</B></DT>" );
sb.append( "<DD>" ).append( value ).append( "</DD>" );
}
}
sb.append( "</DL>" );
}
/**
* @param text not null
* @return <code>true</code> if text contains <code>*</code>, <code>false</code> otherwise.
*/
private static boolean isEveryValues( String text )
{
return text.trim().equals( "*" );
}
/**
* Splits the provided text into a array, using pipe as the separator.
*
* @param text not null
* @return a list of parsed Strings or <code>Collections.EMPTY_LIST</code>.
* By convention, the default value is the first element.
*/
private static List<String> getOnlyValues( String text )
{
if ( text.indexOf( '|' ) == -1 )
{
return Collections.emptyList();
}
List<String> l = new ArrayList<String>();
StringTokenizer token = new StringTokenizer( text, "|" );
while ( token.hasMoreTokens() )
{
l.add( token.nextToken() );
}
return l;
}
/**
* <p>Checks if a String is non <code>null</code> and is
* not empty (<code>length > 0</code>).</p>
*
* @param str the String to check
* @return true if the String is non-null, and not length zero
*/
private static boolean isNotEmpty( String str )
{
return ( str != null && str.length() > 0 );
}
/**
* <p>Checks if a (trimmed) String is <code>null</code> or empty.</p>
*
* @param str the String to check
* @return <code>true</code> if the String is <code>null</code>, or
* length zero once trimmed
*/
private static boolean isEmpty( String str )
{
return ( str == null || str.trim().length() == 0 );
}
}