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