001 package org.apache.maven.tools.plugin.javadoc; 002 003 /* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022 import java.util.ArrayList; 023 import java.util.Arrays; 024 import java.util.Collections; 025 import java.util.Enumeration; 026 import java.util.List; 027 import java.util.StringTokenizer; 028 029 import javax.swing.text.AttributeSet; 030 import javax.swing.text.MutableAttributeSet; 031 import javax.swing.text.SimpleAttributeSet; 032 033 import com.sun.javadoc.Tag; 034 import com.sun.tools.doclets.Taglet; 035 036 /** 037 * Abstract <code>Taglet</code> for <a href="http://maven.codehaus.org/"/>Maven</a> Mojo annotations. 038 * <br/> 039 * A Mojo annotation is defined like the following: 040 * <pre> 041 * @annotation <annotationValue> <parameterName="parameterValue"> 042 * </pre> 043 * 044 * @see <a href="package-summary.html#package_description">package-summary.html</a> 045 * 046 * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a> 047 * @version $Id: AbstractMojoTaglet.java 1338289 2012-05-14 16:53:36Z olamy $ 048 */ 049 public abstract class AbstractMojoTaglet 050 implements Taglet 051 { 052 /** {@inheritDoc} */ 053 public String toString( Tag tag ) 054 { 055 if ( tag == null ) 056 { 057 return null; 058 } 059 060 String tagValue = getTagValue( tag ); 061 MutableAttributeSet tagAttributes = getTagAttributes( tag ); 062 063 StringBuilder sb = new StringBuilder(); 064 065 appendTag( sb, tag, tagAttributes, tagValue ); 066 067 return sb.toString(); 068 } 069 070 /** {@inheritDoc} */ 071 public String toString( Tag[] tags ) 072 { 073 if ( tags.length == 0 ) 074 { 075 return null; 076 } 077 078 StringBuilder sb = new StringBuilder(); 079 for ( int i = 0; i < tags.length; i++ ) 080 { 081 String tagValue = getTagValue( tags[i] ); 082 MutableAttributeSet tagAttributes = getTagAttributes( tags[i] ); 083 084 appendTag( sb, tags[i], tagAttributes, tagValue ); 085 } 086 087 return sb.toString(); 088 } 089 090 /** 091 * @return the header, i.e. the message, to display 092 */ 093 public abstract String getHeader(); 094 095 /** 096 * @return the given annotation value, or <code>null</code> if the given Mojo annotation/tag does't allow 097 * annotation value. 098 * <br/> 099 * <b>Note</b>: the value could be a pattern value, i.e.: <code>*</code> for every values, <code>a|b|c</code> 100 * for <code>a OR b OR c</code>. 101 */ 102 public abstract String getAllowedValue(); 103 104 /** 105 * @return an array of the allowed parameter names for the given Mojo annotation/tag, or <code>null</code> 106 * if the annotation/tag doesn't allow parameter. 107 */ 108 public abstract String[] getAllowedParameterNames(); 109 110 /** 111 * @return <code>true</code> if taglet has annotation value, <code>false</code> otherwise. 112 * @see #getAllowedValue() 113 */ 114 public boolean hasAnnotationValue() 115 { 116 return getAllowedValue() != null; 117 } 118 119 /** 120 * @return <code>true</code> if taglet has parameters, <code>false</code> otherwise. 121 * @see #getAllowedParameterNames() 122 */ 123 public boolean hasAnnotationParameters() 124 { 125 return getAllowedParameterNames() != null; 126 } 127 128 /** 129 * @param tag not null. 130 * @return a not null String or <code>null</code> if no annotation value was found. 131 */ 132 private String getTagValue( Tag tag ) 133 { 134 if ( tag == null ) 135 { 136 throw new IllegalArgumentException( "tag should be not null" ); 137 } 138 139 String text = tag.text(); 140 if ( isEmpty( text ) ) 141 { 142 // using pattern: @annotation 143 return null; 144 } 145 146 String tagValue = null; 147 StringTokenizer token = new StringTokenizer( text, " " ); 148 while ( token.hasMoreTokens() ) 149 { 150 String nextToken = token.nextToken(); 151 152 if ( nextToken.indexOf( "=" ) == -1 ) 153 { 154 // using pattern: @annotation <annotationValue> 155 tagValue = nextToken; 156 } 157 } 158 159 return tagValue; 160 } 161 162 /** 163 * @param tag not null. 164 * @return a not null MutableAttributeSet. 165 */ 166 private MutableAttributeSet getTagAttributes( Tag tag ) 167 { 168 if ( tag == null ) 169 { 170 throw new IllegalArgumentException( "tag should be not null" ); 171 } 172 173 String text = tag.text(); 174 175 StringTokenizer token = new StringTokenizer( text, " " ); 176 MutableAttributeSet tagAttributes = new SimpleAttributeSet(); 177 while ( token.hasMoreTokens() ) 178 { 179 String nextToken = token.nextToken(); 180 181 if ( nextToken.indexOf( "=" ) == -1 ) 182 { 183 // using pattern: @annotation <annotationValue> 184 continue; 185 } 186 187 StringTokenizer token2 = new StringTokenizer( nextToken, "=" ); 188 if ( token2.countTokens() != 2 ) 189 { 190 System.err.println( "The annotation '" + tag.name() + "' has no name/value pairs parameter: " 191 + tag.name() + " " + text + " (" + tag.position().file() + ":" + tag.position().line() + ":" 192 + tag.position().column() + ")" ); 193 tagAttributes.addAttribute( token2.nextToken(), "" ); 194 continue; 195 } 196 197 String name = token2.nextToken(); 198 String value = token2.nextToken().replaceAll( "\"", "" ); 199 200 if ( getAllowedParameterNames() != null && !Arrays.asList( getAllowedParameterNames() ).contains( name ) ) 201 { 202 System.err.println( "The annotation '" + tag.name() + "' has wrong parameter name: " + tag.name() + " " 203 + text + " (" + tag.position().file() + ":" + tag.position().line() + ":" + tag.position().column() 204 + ")" ); 205 } 206 207 tagAttributes.addAttribute( name, value ); 208 } 209 210 return tagAttributes; 211 } 212 213 /** 214 * Append a tag 215 * 216 * @param sb not null 217 * @param tag not null 218 * @param tagAttributes not null 219 * @param tagValue not null 220 */ 221 private void appendTag( StringBuilder sb, Tag tag, MutableAttributeSet tagAttributes, String tagValue ) 222 { 223 if ( !hasAnnotationParameters() ) 224 { 225 if ( tagAttributes.getAttributeCount() > 0 ) 226 { 227 System.err.println( "The annotation '@" + getName() + "' should have no attribute (" 228 + tag.position().file() + ":" + tag.position().line() + ":" + tag.position().column() + ")" ); 229 } 230 231 if ( hasAnnotationValue() ) 232 { 233 sb.append( "<DT><B>" ).append( getHeader() ).append( ":</B></DT>" ); 234 if ( isEveryValues( getAllowedValue() ) ) 235 { 236 if ( isNotEmpty( tagValue ) ) 237 { 238 sb.append( "<DD>" ).append( tagValue ).append( "</DD>" ); 239 } 240 else 241 { 242 System.err.println( "The annotation '@" + getName() + "' is specified to have a value but " 243 + "no value is defined (" + tag.position().file() + ":" + tag.position().line() + ":" 244 + tag.position().column() + ")" ); 245 sb.append( "<DD>" ).append( "NOT DEFINED" ).append( "</DD>" ); 246 } 247 } 248 else 249 { 250 List<String> l = getOnlyValues( getAllowedValue() ); 251 if ( isNotEmpty( tagValue ) ) 252 { 253 if ( l.contains( tagValue ) ) 254 { 255 sb.append( "<DD>" ).append( tagValue ).append( "</DD>" ); 256 } 257 else 258 { 259 System.err.println( "The annotation '@" + getName() + "' is specified to be a value of " 260 + l + " (" + tag.position().file() + ":" + tag.position().line() + ":" 261 + tag.position().column() + ")" ); 262 sb.append( "<DD>" ).append( tagValue ).append( "</DD>" ); 263 } 264 } 265 else 266 { 267 sb.append( "<DD>" ).append( l.get( 0 ) ).append( "</DD>" ); 268 } 269 } 270 } 271 else 272 { 273 if ( isNotEmpty( tagValue ) ) 274 { 275 System.err.println( "The annotation '@" + getName() + "' should have no value (" 276 + tag.position().file() + ":" + tag.position().line() + ":" + tag.position().column() + ")" ); 277 } 278 sb.append( "<DT><B>" ).append( getHeader() ).append( "</B></DT>" ); 279 sb.append( "<DD></DD>" ); 280 } 281 } 282 else 283 { 284 if ( hasAnnotationValue() ) 285 { 286 sb.append( "<DT><B>" ).append( getHeader() ).append( ":</B></DT>" ); 287 if ( isEveryValues( getAllowedValue() ) ) 288 { 289 if ( isNotEmpty( tagValue ) ) 290 { 291 sb.append( "<DD>" ).append( tagValue ); 292 } 293 else 294 { 295 System.err.println( "The annotation '@" + getName() + "' is specified to have a value but " 296 + "no value is defined (" + tag.position().file() + ":" + tag.position().line() + ":" 297 + tag.position().column() + ")" ); 298 sb.append( "<DD>" ).append( "NOT DEFINED" ); 299 } 300 } 301 else 302 { 303 List<String> l = getOnlyValues( getAllowedValue() ); 304 if ( isNotEmpty( tagValue ) ) 305 { 306 if ( l.contains( tagValue ) ) 307 { 308 sb.append( "<DD>" ).append( tagValue ); 309 } 310 else 311 { 312 System.err.println( "The annotation '@" + getName() + "' is specified to be a value in " 313 + l + " (" + tag.position().file() + ":" + tag.position().line() + ":" 314 + tag.position().column() + ")" ); 315 sb.append( "<DD>" ).append( tagValue ); 316 } 317 } 318 else 319 { 320 sb.append( "<DD>" ).append( l.get( 0 ) ); 321 } 322 } 323 } 324 else 325 { 326 if ( isNotEmpty( tagValue ) ) 327 { 328 System.err.println( "The annotation '@" + getName() + "' should have no value (" 329 + tag.position().file() + ":" + tag.position().line() + ":" + tag.position().column() + ")" ); 330 } 331 sb.append( "<DT><B>" ).append( getHeader() ).append( ":</B></DT>" ); 332 sb.append( "<DD>" ); 333 } 334 335 appendAnnotationParameters( sb, tagAttributes ); 336 sb.append( "</DD>" ); 337 } 338 } 339 340 /** 341 * Append the annotation parameters as a definition list. 342 * 343 * @param sb not null 344 * @param att not null 345 */ 346 private static void appendAnnotationParameters( StringBuilder sb, MutableAttributeSet att ) 347 { 348 sb.append( "<DL>" ); 349 350 Enumeration<?> names = att.getAttributeNames(); 351 while ( names.hasMoreElements() ) 352 { 353 Object key = names.nextElement(); 354 Object value = att.getAttribute( key ); 355 356 if ( value instanceof AttributeSet ) 357 { 358 // ignored 359 } 360 else 361 { 362 sb.append( "<DT><B>" ).append( key ).append( ":</B></DT>" ); 363 sb.append( "<DD>" ).append( value ).append( "</DD>" ); 364 } 365 } 366 367 sb.append( "</DL>" ); 368 } 369 370 /** 371 * @param text not null 372 * @return <code>true</code> if text contains <code>*</code>, <code>false</code> otherwise. 373 */ 374 private static boolean isEveryValues( String text ) 375 { 376 return text.trim().equals( "*" ); 377 } 378 379 /** 380 * Splits the provided text into a array, using pipe as the separator. 381 * 382 * @param text not null 383 * @return a list of parsed Strings or <code>Collections.EMPTY_LIST</code>. 384 * By convention, the default value is the first element. 385 */ 386 private static List<String> getOnlyValues( String text ) 387 { 388 if ( text.indexOf( "|" ) == -1 ) 389 { 390 return Collections.emptyList(); 391 } 392 393 List<String> l = new ArrayList<String>(); 394 StringTokenizer token = new StringTokenizer( text, "|" ); 395 while ( token.hasMoreTokens() ) 396 { 397 l.add( token.nextToken() ); 398 } 399 400 return l; 401 } 402 403 /** 404 * <p>Checks if a String is non <code>null</code> and is 405 * not empty (<code>length > 0</code>).</p> 406 * 407 * @param str the String to check 408 * @return true if the String is non-null, and not length zero 409 */ 410 private static boolean isNotEmpty( String str ) 411 { 412 return ( str != null && str.length() > 0 ); 413 } 414 415 /** 416 * <p>Checks if a (trimmed) String is <code>null</code> or empty.</p> 417 * 418 * @param str the String to check 419 * @return <code>true</code> if the String is <code>null</code>, or 420 * length zero once trimmed 421 */ 422 private static boolean isEmpty( String str ) 423 { 424 return ( str == null || str.trim().length() == 0 ); 425 } 426 }