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