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> * @annotation <annotationValue> <parameterName="parameterValue"> * </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 ); } }