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 1405630 2012-11-04 20:38:18Z rfscholte $
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 }