1 package org.apache.maven.tools.plugin.extractor.java;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import com.thoughtworks.qdox.JavaDocBuilder;
23 import com.thoughtworks.qdox.model.DocletTag;
24 import com.thoughtworks.qdox.model.JavaClass;
25 import com.thoughtworks.qdox.model.JavaField;
26 import com.thoughtworks.qdox.model.JavaSource;
27 import com.thoughtworks.qdox.model.Type;
28
29 import org.apache.maven.plugin.descriptor.InvalidParameterException;
30 import org.apache.maven.plugin.descriptor.InvalidPluginDescriptorException;
31 import org.apache.maven.plugin.descriptor.MojoDescriptor;
32 import org.apache.maven.plugin.descriptor.Parameter;
33 import org.apache.maven.plugin.descriptor.PluginDescriptor;
34 import org.apache.maven.plugin.descriptor.Requirement;
35 import org.apache.maven.project.MavenProject;
36 import org.apache.maven.tools.plugin.extractor.MojoDescriptorExtractor;
37 import org.codehaus.plexus.logging.AbstractLogEnabled;
38 import org.codehaus.plexus.util.StringUtils;
39
40 import java.io.File;
41 import java.util.ArrayList;
42 import java.util.Iterator;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.TreeMap;
46
47
48 /**
49 * @todo add example usage tag that can be shown in the doco
50 * @todo need to add validation directives so that systems embedding maven2 can
51 * get validation directives to help users in IDEs.
52 */
53 public class JavaMojoDescriptorExtractor
54 extends AbstractLogEnabled
55 implements MojoDescriptorExtractor
56 {
57 public static final String MAVEN_PLUGIN_INSTANTIATION = "instantiationStrategy";
58
59 public static final String CONFIGURATOR = "configurator";
60
61 public static final String PARAMETER = "parameter";
62
63 public static final String PARAMETER_EXPRESSION = "expression";
64
65 public static final String PARAMETER_DEFAULT_VALUE = "default-value";
66
67 public static final String PARAMETER_ALIAS = "alias";
68
69 public static final String SINCE = "since";
70
71 /**
72 * This indicates the base name of the bean properties used to read/write this parameter's value.
73 * So:
74 *
75 * @parameter property="project"
76 * <p/>
77 * Would say there is a getProject() method and a setProject(Project) method. Here the field
78 * name would not be the basis for the parameter's name. This mode of operation will allow the
79 * mojos to be usable as beans and will be the promoted form of use.
80 */
81 public static final String PARAMETER_PROPERTY = "property";
82
83 public static final String REQUIRED = "required";
84
85 public static final String DEPRECATED = "deprecated";
86
87 public static final String READONLY = "readonly";
88
89 public static final String GOAL = "goal";
90
91 public static final String PHASE = "phase";
92
93 public static final String EXECUTE = "execute";
94
95 public static final String EXECUTE_LIFECYCLE = "lifecycle";
96
97 public static final String EXECUTE_PHASE = "phase";
98
99 public static final String EXECUTE_GOAL = "goal";
100
101 public static final String GOAL_DESCRIPTION = "description";
102
103 public static final String GOAL_REQUIRES_DEPENDENCY_RESOLUTION = "requiresDependencyResolution";
104
105 public static final String GOAL_REQUIRES_PROJECT = "requiresProject";
106
107 public static final String GOAL_REQUIRES_REPORTS = "requiresReports";
108
109 public static final String GOAL_IS_AGGREGATOR = "aggregator";
110
111 public static final String GOAL_REQUIRES_ONLINE = "requiresOnline";
112
113 public static final String GOAL_INHERIT_BY_DEFAULT = "inheritByDefault";
114
115 public static final String GOAL_MULTI_EXECUTION_STRATEGY = "attainAlways";
116
117 public static final String GOAL_REQUIRES_DIRECT_INVOCATION = "requiresDirectInvocation";
118
119 private static final String COMPONENT = "component";
120
121 private static final String COMPONENT_ROLE = "role";
122
123 private static final String COMPONENT_ROLEHINT = "roleHint";
124
125 protected void validateParameter( Parameter parameter, int i )
126 throws InvalidParameterException
127 {
128
129 String name = parameter.getName();
130
131 if ( name == null )
132 {
133 throw new InvalidParameterException( "name", i );
134 }
135
136
137 String type = parameter.getType();
138
139 if ( type == null )
140 {
141 throw new InvalidParameterException( "type", i );
142 }
143
144
145 String description = parameter.getDescription();
146
147 if ( description == null )
148 {
149 throw new InvalidParameterException( "description", i );
150 }
151 }
152
153
154
155
156
157 private MojoDescriptor createMojoDescriptor( JavaSource javaSource, PluginDescriptor pluginDescriptor )
158 throws InvalidPluginDescriptorException
159 {
160 MojoDescriptor mojoDescriptor = new MojoDescriptor();
161
162 mojoDescriptor.setPluginDescriptor( pluginDescriptor );
163
164 JavaClass javaClass = getJavaClass( javaSource );
165
166 mojoDescriptor.setLanguage( "java" );
167
168 mojoDescriptor.setImplementation( javaClass.getFullyQualifiedName() );
169
170 mojoDescriptor.setDescription( javaClass.getComment() );
171
172 DocletTag tag = findInClassHierarchy( javaClass, MAVEN_PLUGIN_INSTANTIATION );
173
174 if ( tag != null )
175 {
176 mojoDescriptor.setInstantiationStrategy( tag.getValue() );
177 }
178
179 tag = findInClassHierarchy( javaClass, GOAL_MULTI_EXECUTION_STRATEGY );
180
181 if ( tag != null )
182 {
183 mojoDescriptor.setExecutionStrategy( MojoDescriptor.MULTI_PASS_EXEC_STRATEGY );
184 }
185 else
186 {
187 mojoDescriptor.setExecutionStrategy( MojoDescriptor.SINGLE_PASS_EXEC_STRATEGY );
188 }
189
190
191
192
193
194 DocletTag configurator = findInClassHierarchy( javaClass, CONFIGURATOR );
195
196 if ( configurator != null )
197 {
198 mojoDescriptor.setComponentConfigurator( configurator.getValue() );
199 }
200
201
202
203
204
205 DocletTag goal = findInClassHierarchy( javaClass, GOAL );
206
207 if ( goal != null )
208 {
209 mojoDescriptor.setGoal( goal.getValue() );
210 }
211
212
213
214
215
216 DocletTag phase = findInClassHierarchy( javaClass, PHASE );
217
218 if ( phase != null )
219 {
220 mojoDescriptor.setPhase( phase.getValue() );
221 }
222
223
224
225
226
227 DocletTag execute = findInClassHierarchy( javaClass, EXECUTE );
228
229 if ( execute != null )
230 {
231 String executePhase = execute.getNamedParameter( EXECUTE_PHASE );
232 String executeGoal = execute.getNamedParameter( EXECUTE_GOAL );
233
234 if ( executePhase == null && executeGoal == null )
235 {
236 throw new InvalidPluginDescriptorException( "@execute tag requires a 'phase' or 'goal' parameter" );
237 }
238 else if ( executePhase != null && executeGoal != null )
239 {
240 throw new InvalidPluginDescriptorException(
241 "@execute tag can have only one of a 'phase' or 'goal' parameter" );
242 }
243 mojoDescriptor.setExecutePhase( executePhase );
244 mojoDescriptor.setExecuteGoal( executeGoal );
245
246 String lifecycle = execute.getNamedParameter( EXECUTE_LIFECYCLE );
247
248 if ( lifecycle != null )
249 {
250 mojoDescriptor.setExecuteLifecycle( lifecycle );
251 if ( mojoDescriptor.getExecuteGoal() != null )
252 {
253 throw new InvalidPluginDescriptorException(
254 "@execute lifecycle requires a phase instead of a goal" );
255 }
256 }
257 }
258
259
260
261
262
263 DocletTag requiresDependencyResolution = findInClassHierarchy( javaClass, GOAL_REQUIRES_DEPENDENCY_RESOLUTION );
264
265 if ( requiresDependencyResolution != null )
266 {
267 String value = requiresDependencyResolution.getValue();
268
269 if ( value == null || value.length() == 0 )
270 {
271 value = "runtime";
272 }
273
274 mojoDescriptor.setDependencyResolutionRequired( value );
275 }
276
277
278
279
280
281 boolean value = getBooleanTagValue( javaClass, GOAL_REQUIRES_PROJECT, mojoDescriptor.isProjectRequired() );
282 mojoDescriptor.setProjectRequired( value );
283
284
285
286
287
288 DocletTag aggregator = findInClassHierarchy( javaClass, GOAL_IS_AGGREGATOR );
289
290 if ( aggregator != null )
291 {
292 mojoDescriptor.setAggregator( true );
293 }
294
295
296
297
298
299 value =
300 getBooleanTagValue( javaClass, GOAL_REQUIRES_DIRECT_INVOCATION, mojoDescriptor.isDirectInvocationOnly() );
301 mojoDescriptor.setDirectInvocationOnly( value );
302
303
304
305
306
307 value = getBooleanTagValue( javaClass, GOAL_REQUIRES_ONLINE, mojoDescriptor.isOnlineRequired() );
308 mojoDescriptor.setOnlineRequired( value );
309
310
311
312
313
314 value = getBooleanTagValue( javaClass, GOAL_INHERIT_BY_DEFAULT, mojoDescriptor.isInheritedByDefault() );
315 mojoDescriptor.setInheritedByDefault( value );
316
317 extractParameters( mojoDescriptor, javaClass );
318
319 return mojoDescriptor;
320 }
321
322 private static boolean getBooleanTagValue( JavaClass javaClass, String tagName, boolean defaultValue )
323 {
324 DocletTag requiresProject = findInClassHierarchy( javaClass, tagName );
325
326 if ( requiresProject != null )
327 {
328 String requiresProjectValue = requiresProject.getValue();
329
330 if ( requiresProjectValue != null && requiresProjectValue.length() > 0 )
331 {
332 defaultValue = Boolean.valueOf( requiresProjectValue ).booleanValue();
333 }
334 }
335 return defaultValue;
336 }
337
338 private static DocletTag findInClassHierarchy( JavaClass javaClass, String tagName )
339 {
340 DocletTag tag = javaClass.getTagByName( tagName );
341
342 if ( tag == null )
343 {
344 JavaClass superClass = javaClass.getSuperJavaClass();
345
346 if ( superClass != null )
347 {
348 tag = findInClassHierarchy( superClass, tagName );
349 }
350 }
351
352 return tag;
353 }
354
355 private void extractParameters( MojoDescriptor mojoDescriptor, JavaClass javaClass )
356 throws InvalidPluginDescriptorException
357 {
358
359
360
361
362 Map rawParams = extractFieldParameterTags( javaClass );
363
364 for ( Iterator it = rawParams.entrySet().iterator(); it.hasNext(); )
365 {
366 Map.Entry entry = (Map.Entry) it.next();
367
368 JavaField field = (JavaField) entry.getValue();
369
370 Type type = field.getType();
371
372 Parameter pd = new Parameter();
373
374 if ( !type.isArray() )
375 {
376 pd.setType( type.getValue() );
377 }
378 else
379 {
380 StringBuffer value = new StringBuffer( type.getValue() );
381
382 int remaining = type.getDimensions();
383
384 while ( remaining-- > 0 )
385 {
386 value.append( "[]" );
387 }
388
389 pd.setType( value.toString() );
390 }
391
392 pd.setDescription( field.getComment() );
393
394 DocletTag componentTag = field.getTagByName( COMPONENT );
395
396 if ( componentTag != null )
397 {
398 String role = componentTag.getNamedParameter( COMPONENT_ROLE );
399
400 if ( role == null )
401 {
402 role = field.getType().toString();
403 }
404
405 String roleHint = componentTag.getNamedParameter( COMPONENT_ROLEHINT );
406
407 if ( roleHint == null )
408 {
409
410 roleHint = componentTag.getNamedParameter( "role-hint" );
411 }
412
413 pd.setRequirement( new Requirement( role, roleHint ) );
414
415 pd.setName( (String) entry.getKey() );
416 }
417 else
418 {
419 DocletTag parameter = field.getTagByName( PARAMETER );
420
421
422
423
424
425
426
427
428
429
430
431 String property = parameter.getNamedParameter( PARAMETER_PROPERTY );
432
433 if ( !StringUtils.isEmpty( property ) )
434 {
435 pd.setName( property );
436 }
437 else
438 {
439 pd.setName( (String) entry.getKey() );
440 }
441
442 pd.setRequired( field.getTagByName( REQUIRED ) != null );
443
444 pd.setEditable( field.getTagByName( READONLY ) == null );
445
446 DocletTag deprecationTag = field.getTagByName( DEPRECATED );
447
448 if ( deprecationTag != null )
449 {
450 pd.setDeprecated( deprecationTag.getValue() );
451 }
452
453 String alias = parameter.getNamedParameter( PARAMETER_ALIAS );
454
455 if ( !StringUtils.isEmpty( alias ) )
456 {
457 pd.setAlias( alias );
458 }
459
460 pd.setExpression( parameter.getNamedParameter( PARAMETER_EXPRESSION ) );
461
462 if ( "${reports}".equals( pd.getExpression() ) )
463 {
464 mojoDescriptor.setRequiresReports( true );
465 }
466
467 pd.setDefaultValue( parameter.getNamedParameter( PARAMETER_DEFAULT_VALUE ) );
468 }
469
470 mojoDescriptor.addParameter( pd );
471 }
472 }
473
474 private Map extractFieldParameterTags( JavaClass javaClass )
475 {
476 Map rawParams;
477
478
479
480 JavaClass superClass = javaClass.getSuperJavaClass();
481
482 if ( superClass != null )
483 {
484 rawParams = extractFieldParameterTags( superClass );
485 }
486 else
487 {
488 rawParams = new TreeMap();
489 }
490
491 JavaField[] classFields = javaClass.getFields();
492
493 if ( classFields != null )
494 {
495 for ( int i = 0; i < classFields.length; i++ )
496 {
497 JavaField field = classFields[i];
498
499 if ( field.getTagByName( PARAMETER ) != null || field.getTagByName( COMPONENT ) != null )
500 {
501 rawParams.put( field.getName(), field );
502 }
503 }
504 }
505 return rawParams;
506 }
507
508 private JavaClass getJavaClass( JavaSource javaSource )
509 {
510 return javaSource.getClasses()[0];
511 }
512
513 public List execute( MavenProject project, PluginDescriptor pluginDescriptor )
514 throws InvalidPluginDescriptorException
515 {
516 JavaDocBuilder builder = new JavaDocBuilder();
517
518 for ( Iterator i = project.getCompileSourceRoots().iterator(); i.hasNext(); )
519 {
520 builder.addSourceTree( new File( (String) i.next() ) );
521 }
522
523 JavaSource[] javaSources = builder.getSources();
524
525 List descriptors = new ArrayList();
526
527 for ( int i = 0; i < javaSources.length; i++ )
528 {
529 JavaClass javaClass = getJavaClass( javaSources[i] );
530
531 DocletTag tag = javaClass.getTagByName( GOAL );
532
533 if ( tag != null )
534 {
535 MojoDescriptor mojoDescriptor = createMojoDescriptor( javaSources[i], pluginDescriptor );
536
537
538
539
540
541
542 List parameters = mojoDescriptor.getParameters();
543
544 if ( parameters != null )
545 {
546 for ( int j = 0; j < parameters.size(); j++ )
547 {
548 validateParameter( (Parameter) parameters.get( j ), j );
549 }
550 }
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574 descriptors.add( mojoDescriptor );
575 }
576 }
577
578 return descriptors;
579 }
580
581 }