1 package org.apache.maven.tools.plugin.generator;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.plugin.descriptor.MojoDescriptor;
23 import org.apache.maven.plugin.descriptor.Parameter;
24 import org.apache.maven.project.MavenProject;
25 import org.apache.maven.tools.plugin.ExtendedMojoDescriptor;
26 import org.apache.maven.tools.plugin.PluginToolsRequest;
27 import org.codehaus.plexus.util.StringUtils;
28 import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
29 import org.codehaus.plexus.util.xml.XMLWriter;
30
31 import java.io.File;
32 import java.io.FileOutputStream;
33 import java.io.IOException;
34 import java.io.OutputStreamWriter;
35 import java.io.PrintWriter;
36 import java.io.Writer;
37 import java.text.MessageFormat;
38 import java.util.ArrayList;
39 import java.util.Iterator;
40 import java.util.List;
41 import java.util.Locale;
42 import java.util.ResourceBundle;
43
44
45
46
47 public class PluginXdocGenerator
48 implements Generator
49 {
50
51
52
53 private final Locale locale;
54
55
56
57
58 private final MavenProject project;
59
60
61
62
63
64 public PluginXdocGenerator()
65 {
66 this.project = null;
67 this.locale = Locale.ENGLISH;
68 }
69
70
71
72
73
74
75 public PluginXdocGenerator( MavenProject project )
76 {
77 this.project = project;
78 this.locale = Locale.ENGLISH;
79 }
80
81
82
83
84
85 public PluginXdocGenerator( MavenProject project, Locale locale )
86 {
87 this.project = project;
88 if ( locale == null )
89 {
90 this.locale = Locale.ENGLISH;
91 }
92 else
93 {
94 this.locale = locale;
95 }
96 }
97
98
99
100
101
102 public void execute( File destinationDirectory, PluginToolsRequest request )
103 throws GeneratorException
104 {
105 try
106 {
107 if ( request.getPluginDescriptor().getMojos() != null )
108 {
109 @SuppressWarnings( "unchecked" ) List<MojoDescriptor> mojos = request.getPluginDescriptor().getMojos();
110
111 for ( MojoDescriptor descriptor : mojos )
112 {
113 processMojoDescriptor( descriptor, destinationDirectory );
114 }
115 }
116 }
117 catch ( IOException e )
118 {
119 throw new GeneratorException( e.getMessage(), e );
120 }
121
122 }
123
124
125
126
127
128
129 protected void processMojoDescriptor( MojoDescriptor mojoDescriptor, File destinationDirectory )
130 throws IOException
131 {
132 File outputFile = new File( destinationDirectory, getMojoFilename( mojoDescriptor, "xml" ) );
133 String encoding = "UTF-8";
134 try ( Writer writer = new OutputStreamWriter( new FileOutputStream( outputFile ), encoding ) )
135 {
136
137 XMLWriter w = new PrettyPrintXMLWriter( new PrintWriter( writer ), encoding, null );
138 writeBody( mojoDescriptor, w );
139
140 writer.flush();
141 }
142 }
143
144
145
146
147
148
149 private String getMojoFilename( MojoDescriptor mojo, String ext )
150 {
151 return mojo.getGoal() + "-mojo." + ext;
152 }
153
154
155
156
157
158 private void writeBody( MojoDescriptor mojoDescriptor, XMLWriter w )
159 {
160 w.startElement( "document" );
161 w.addAttribute( "xmlns", "http://maven.apache.org/XDOC/2.0" );
162 w.addAttribute( "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" );
163 w.addAttribute( "xsi:schemaLocation",
164 "http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd" );
165
166
167
168
169
170 w.startElement( "properties" );
171
172 w.startElement( "title" );
173 w.writeText( mojoDescriptor.getFullGoalName() );
174 w.endElement();
175
176 w.endElement();
177
178
179
180
181
182 w.startElement( "body" );
183
184 w.startElement( "section" );
185
186 w.addAttribute( "name", mojoDescriptor.getFullGoalName() );
187
188 writeReportNotice( mojoDescriptor, w );
189
190 w.startElement( "p" );
191 w.writeMarkup( getString( "pluginxdoc.mojodescriptor.fullname" ) );
192 w.endElement();
193 w.startElement( "p" );
194 w.writeMarkup( mojoDescriptor.getPluginDescriptor().getGroupId() + ":"
195 + mojoDescriptor.getPluginDescriptor().getArtifactId() + ":"
196 + mojoDescriptor.getPluginDescriptor().getVersion() + ":" + mojoDescriptor.getGoal() );
197 w.endElement();
198
199 if ( StringUtils.isNotEmpty( mojoDescriptor.getDeprecated() ) )
200 {
201 w.startElement( "p" );
202 w.writeMarkup( getString( "pluginxdoc.mojodescriptor.deprecated" ) );
203 w.endElement();
204 w.startElement( "div" );
205 w.writeMarkup( GeneratorUtils.makeHtmlValid( mojoDescriptor.getDeprecated() ) );
206 w.endElement();
207 }
208
209 w.startElement( "p" );
210 w.writeMarkup( getString( "pluginxdoc.description" ) );
211 w.endElement();
212 w.startElement( "div" );
213 if ( StringUtils.isNotEmpty( mojoDescriptor.getDescription() ) )
214 {
215 w.writeMarkup( GeneratorUtils.makeHtmlValid( mojoDescriptor.getDescription() ) );
216 }
217 else
218 {
219 w.writeText( getString( "pluginxdoc.nodescription" ) );
220 }
221 w.endElement();
222
223 writeGoalAttributes( mojoDescriptor, w );
224
225 writeGoalParameterTable( mojoDescriptor, w );
226
227 w.endElement();
228
229 w.endElement();
230
231 w.endElement();
232 }
233
234
235
236
237
238 private void writeReportNotice( MojoDescriptor mojoDescriptor, XMLWriter w )
239 {
240 if ( GeneratorUtils.isMavenReport( mojoDescriptor.getImplementation(), project ) )
241 {
242 w.startElement( "p" );
243 w.writeMarkup( getString( "pluginxdoc.mojodescriptor.notice.note" ) );
244 w.writeText( getString( "pluginxdoc.mojodescriptor.notice.isMavenReport" ) );
245 w.endElement();
246 }
247 }
248
249
250
251
252
253 private void writeGoalAttributes( MojoDescriptor mojoDescriptor, XMLWriter w )
254 {
255 w.startElement( "p" );
256 w.writeMarkup( getString( "pluginxdoc.mojodescriptor.attributes" ) );
257 w.endElement();
258
259 boolean addedUl = false;
260 String value;
261 if ( mojoDescriptor.isProjectRequired() )
262 {
263 addedUl = addUl( w, addedUl );
264 w.startElement( "li" );
265 w.writeMarkup( getString( "pluginxdoc.mojodescriptor.projectRequired" ) );
266 w.endElement();
267 }
268
269 if ( mojoDescriptor.isRequiresReports() )
270 {
271 addedUl = addUl( w, addedUl );
272 w.startElement( "li" );
273 w.writeMarkup( getString( "pluginxdoc.mojodescriptor.reportingMojo" ) );
274 w.endElement();
275 }
276
277 if ( mojoDescriptor.isAggregator() )
278 {
279 addedUl = addUl( w, addedUl );
280 w.startElement( "li" );
281 w.writeMarkup( getString( "pluginxdoc.mojodescriptor.aggregator" ) );
282 w.endElement();
283 }
284
285 if ( mojoDescriptor.isDirectInvocationOnly() )
286 {
287 addedUl = addUl( w, addedUl );
288 w.startElement( "li" );
289 w.writeMarkup( getString( "pluginxdoc.mojodescriptor.directInvocationOnly" ) );
290 w.endElement();
291 }
292
293 value = mojoDescriptor.isDependencyResolutionRequired();
294 if ( StringUtils.isNotEmpty( value ) )
295 {
296 addedUl = addUl( w, addedUl );
297 w.startElement( "li" );
298 w.writeMarkup( format( "pluginxdoc.mojodescriptor.dependencyResolutionRequired", value ) );
299 w.endElement();
300 }
301
302 if ( mojoDescriptor instanceof ExtendedMojoDescriptor )
303 {
304 ExtendedMojoDescriptor extendedMojoDescriptor = (ExtendedMojoDescriptor) mojoDescriptor;
305
306 value = extendedMojoDescriptor.getDependencyCollectionRequired();
307 if ( StringUtils.isNotEmpty( value ) )
308 {
309 addedUl = addUl( w, addedUl );
310 w.startElement( "li" );
311 w.writeMarkup( format( "pluginxdoc.mojodescriptor.dependencyCollectionRequired", value ) );
312 w.endElement();
313 }
314
315 if ( extendedMojoDescriptor.isThreadSafe() )
316 {
317 addedUl = addUl( w, addedUl );
318 w.startElement( "li" );
319 w.writeMarkup( getString( "pluginxdoc.mojodescriptor.threadSafe" ) );
320 w.endElement();
321 }
322
323 }
324
325 value = mojoDescriptor.getSince();
326 if ( StringUtils.isNotEmpty( value ) )
327 {
328 addedUl = addUl( w, addedUl );
329 w.startElement( "li" );
330 w.writeMarkup( format( "pluginxdoc.mojodescriptor.since", value ) );
331 w.endElement();
332 }
333
334 value = mojoDescriptor.getPhase();
335 if ( StringUtils.isNotEmpty( value ) )
336 {
337 addedUl = addUl( w, addedUl );
338 w.startElement( "li" );
339 w.writeMarkup( format( "pluginxdoc.mojodescriptor.phase", value ) );
340 w.endElement();
341 }
342
343 value = mojoDescriptor.getExecutePhase();
344 if ( StringUtils.isNotEmpty( value ) )
345 {
346 addedUl = addUl( w, addedUl );
347 w.startElement( "li" );
348 w.writeMarkup( format( "pluginxdoc.mojodescriptor.executePhase", value ) );
349 w.endElement();
350 }
351
352 value = mojoDescriptor.getExecuteGoal();
353 if ( StringUtils.isNotEmpty( value ) )
354 {
355 addedUl = addUl( w, addedUl );
356 w.startElement( "li" );
357 w.writeMarkup( format( "pluginxdoc.mojodescriptor.executeGoal", value ) );
358 w.endElement();
359 }
360
361 value = mojoDescriptor.getExecuteLifecycle();
362 if ( StringUtils.isNotEmpty( value ) )
363 {
364 addedUl = addUl( w, addedUl );
365 w.startElement( "li" );
366 w.writeMarkup( format( "pluginxdoc.mojodescriptor.executeLifecycle", value ) );
367 w.endElement();
368 }
369
370 if ( mojoDescriptor.isOnlineRequired() )
371 {
372 addedUl = addUl( w, addedUl );
373 w.startElement( "li" );
374 w.writeMarkup( getString( "pluginxdoc.mojodescriptor.onlineRequired" ) );
375 w.endElement();
376 }
377
378 if ( !mojoDescriptor.isInheritedByDefault() )
379 {
380 addedUl = addUl( w, addedUl );
381 w.startElement( "li" );
382 w.writeMarkup( getString( "pluginxdoc.mojodescriptor.inheritedByDefault" ) );
383 w.endElement();
384 }
385
386 if ( addedUl )
387 {
388 w.endElement();
389 }
390 }
391
392
393
394
395
396 private void writeGoalParameterTable( MojoDescriptor mojoDescriptor, XMLWriter w )
397 {
398 List<Parameter> parameterList = mojoDescriptor.getParameters();
399
400
401 List<Parameter> list = filterParameters( parameterList );
402
403 if ( !list.isEmpty() )
404 {
405 writeParameterSummary( mojoDescriptor, list, w );
406
407 writeParameterDetails( mojoDescriptor, list, w );
408 }
409 else
410 {
411 w.startElement( "subsection" );
412 w.addAttribute( "name", getString( "pluginxdoc.mojodescriptor.parameters" ) );
413
414 w.startElement( "p" );
415 w.writeMarkup( getString( "pluginxdoc.mojodescriptor.noParameter" ) );
416 w.endElement();
417
418 w.endElement();
419 }
420 }
421
422
423
424
425
426
427
428 private List<Parameter> filterParameters( List<Parameter> parameterList )
429 {
430 List<Parameter> filtered = new ArrayList<>();
431
432 if ( parameterList != null )
433 {
434 for ( Parameter parameter : parameterList )
435 {
436 if ( parameter.isEditable() )
437 {
438 String expression = parameter.getExpression();
439
440 if ( expression == null || !expression.startsWith( "${component." ) )
441 {
442 filtered.add( parameter );
443 }
444 }
445 }
446 }
447
448 return filtered;
449 }
450
451
452
453
454
455
456 private void writeParameterDetails( MojoDescriptor mojoDescriptor, List<Parameter> parameterList, XMLWriter w )
457 {
458 w.startElement( "subsection" );
459 w.addAttribute( "name", getString( "pluginxdoc.mojodescriptor.parameter.details" ) );
460
461 for ( Iterator<Parameter> parameters = parameterList.iterator(); parameters.hasNext(); )
462 {
463 Parameter parameter = parameters.next();
464
465 w.startElement( "h4" );
466 w.writeMarkup( format( "pluginxdoc.mojodescriptor.parameter.name_internal", parameter.getName() ) );
467 w.endElement();
468
469 if ( StringUtils.isNotEmpty( parameter.getDeprecated() ) )
470 {
471 w.startElement( "div" );
472 w.writeMarkup( format( "pluginxdoc.mojodescriptor.parameter.deprecated",
473 GeneratorUtils.makeHtmlValid( parameter.getDeprecated() ) ) );
474 w.endElement();
475 }
476
477 w.startElement( "div" );
478 if ( StringUtils.isNotEmpty( parameter.getDescription() ) )
479 {
480 w.writeMarkup( GeneratorUtils.makeHtmlValid( parameter.getDescription() ) );
481 }
482 else
483 {
484 w.writeMarkup( getString( "pluginxdoc.nodescription" ) );
485 }
486 w.endElement();
487
488 boolean addedUl = false;
489 addedUl = addUl( w, addedUl, parameter.getType() );
490 writeDetail( getString( "pluginxdoc.mojodescriptor.parameter.type" ), parameter.getType(), w );
491
492 if ( StringUtils.isNotEmpty( parameter.getSince() ) )
493 {
494 addedUl = addUl( w, addedUl );
495 writeDetail( getString( "pluginxdoc.mojodescriptor.parameter.since" ), parameter.getSince(), w );
496 }
497 else
498 {
499 if ( StringUtils.isNotEmpty( mojoDescriptor.getSince() ) )
500 {
501 addedUl = addUl( w, addedUl );
502 writeDetail( getString( "pluginxdoc.mojodescriptor.parameter.since" ), mojoDescriptor.getSince(),
503 w );
504 }
505 }
506
507 if ( parameter.isRequired() )
508 {
509 addedUl = addUl( w, addedUl );
510 writeDetail( getString( "pluginxdoc.mojodescriptor.parameter.required" ), getString( "pluginxdoc.yes" ),
511 w );
512 }
513 else
514 {
515 addedUl = addUl( w, addedUl );
516 writeDetail( getString( "pluginxdoc.mojodescriptor.parameter.required" ), getString( "pluginxdoc.no" ),
517 w );
518 }
519
520 String expression = parameter.getExpression();
521 addedUl = addUl( w, addedUl, expression );
522 String property = getPropertyFromExpression( expression );
523 if ( property == null )
524 {
525 writeDetail( getString( "pluginxdoc.mojodescriptor.parameter.expression" ), expression, w );
526 }
527 else
528 {
529 writeDetail( getString( "pluginxdoc.mojodescriptor.parameter.property" ), property, w );
530 }
531
532 addedUl = addUl( w, addedUl, parameter.getDefaultValue() );
533 writeDetail( getString( "pluginxdoc.mojodescriptor.parameter.default" ),
534 escapeXml( parameter.getDefaultValue() ), w );
535
536 addedUl = addUl( w, addedUl, parameter.getAlias() );
537 writeDetail( getString( "pluginxdoc.mojodescriptor.parameter.alias" ), escapeXml( parameter.getAlias() ),
538 w );
539
540 if ( addedUl )
541 {
542 w.endElement();
543 }
544
545 if ( parameters.hasNext() )
546 {
547 w.writeMarkup( "<hr/>" );
548 }
549 }
550
551 w.endElement();
552 }
553
554 private boolean addUl( XMLWriter w, boolean addedUl, String content )
555 {
556 if ( StringUtils.isNotEmpty( content ) )
557 {
558 return addUl( w, addedUl );
559 }
560 return addedUl;
561 }
562
563 private boolean addUl( XMLWriter w, boolean addedUl )
564 {
565 if ( !addedUl )
566 {
567 w.startElement( "ul" );
568 addedUl = true;
569 }
570 return addedUl;
571 }
572
573 private String getPropertyFromExpression( String expression )
574 {
575 if ( StringUtils.isNotEmpty( expression ) && expression.startsWith( "${" ) && expression.endsWith( "}" )
576 && !expression.substring( 2 ).contains( "${" ) )
577 {
578
579 return expression.substring( 2, expression.length() - 1 );
580 }
581
582 return null;
583 }
584
585
586
587
588
589
590 private void writeDetail( String param, String value, XMLWriter w )
591 {
592 if ( StringUtils.isNotEmpty( value ) )
593 {
594 w.startElement( "li" );
595 w.writeMarkup( format( "pluginxdoc.detail", new String[]{ param, value } ) );
596 w.endElement();
597 }
598 }
599
600
601
602
603
604
605 private void writeParameterSummary( MojoDescriptor mojoDescriptor, List<Parameter> parameterList, XMLWriter w )
606 {
607 List<Parameter> requiredParams = getParametersByRequired( true, parameterList );
608 if ( requiredParams.size() > 0 )
609 {
610 writeParameterList( mojoDescriptor, getString( "pluginxdoc.mojodescriptor.requiredParameters" ),
611 requiredParams, w );
612 }
613
614 List<Parameter> optionalParams = getParametersByRequired( false, parameterList );
615 if ( optionalParams.size() > 0 )
616 {
617 writeParameterList( mojoDescriptor, getString( "pluginxdoc.mojodescriptor.optionalParameters" ),
618 optionalParams, w );
619 }
620 }
621
622
623
624
625
626
627
628 private void writeParameterList( MojoDescriptor mojoDescriptor, String title, List<Parameter> parameterList,
629 XMLWriter w )
630 {
631 w.startElement( "subsection" );
632 w.addAttribute( "name", title );
633
634 w.startElement( "table" );
635 w.addAttribute( "border", "0" );
636
637 w.startElement( "tr" );
638 w.startElement( "th" );
639 w.writeText( getString( "pluginxdoc.mojodescriptor.parameter.name" ) );
640 w.endElement();
641 w.startElement( "th" );
642 w.writeText( getString( "pluginxdoc.mojodescriptor.parameter.type" ) );
643 w.endElement();
644 w.startElement( "th" );
645 w.writeText( getString( "pluginxdoc.mojodescriptor.parameter.since" ) );
646 w.endElement();
647 w.startElement( "th" );
648 w.writeText( getString( "pluginxdoc.mojodescriptor.parameter.description" ) );
649 w.endElement();
650 w.endElement();
651
652 for ( Parameter parameter : parameterList )
653 {
654 w.startElement( "tr" );
655
656
657 w.startElement( "td" );
658 w.writeMarkup( format( "pluginxdoc.mojodescriptor.parameter.name_link", parameter.getName() ) );
659 w.endElement();
660
661
662 w.startElement( "td" );
663 int index = parameter.getType().lastIndexOf( "." );
664 w.writeMarkup( "<code>" + parameter.getType().substring( index + 1 ) + "</code>" );
665 w.endElement();
666
667
668 w.startElement( "td" );
669 if ( StringUtils.isNotEmpty( parameter.getSince() ) )
670 {
671 w.writeMarkup( "<code>" + parameter.getSince() + "</code>" );
672 }
673 else
674 {
675 if ( StringUtils.isNotEmpty( mojoDescriptor.getSince() ) )
676 {
677 w.writeMarkup( "<code>" + mojoDescriptor.getSince() + "</code>" );
678 }
679 else
680 {
681 w.writeMarkup( "<code>-</code>" );
682 }
683 }
684 w.endElement();
685
686
687 w.startElement( "td" );
688 String description;
689 if ( StringUtils.isNotEmpty( parameter.getDeprecated() ) )
690 {
691 description = format( "pluginxdoc.mojodescriptor.parameter.deprecated",
692 GeneratorUtils.makeHtmlValid( parameter.getDeprecated() ) );
693 }
694 else if ( StringUtils.isNotEmpty( parameter.getDescription() ) )
695 {
696 description = GeneratorUtils.makeHtmlValid( parameter.getDescription() );
697 }
698 else
699 {
700 description = getString( "pluginxdoc.nodescription" );
701 }
702 w.writeMarkup( description + "<br/>" );
703
704 if ( StringUtils.isNotEmpty( parameter.getDefaultValue() ) )
705 {
706 w.writeMarkup( format( "pluginxdoc.mojodescriptor.parameter.defaultValue",
707 escapeXml( parameter.getDefaultValue() ) ) );
708 w.writeMarkup( "<br/>" );
709 }
710
711 String property = getPropertyFromExpression( parameter.getExpression() );
712 if ( property != null )
713 {
714 w.writeMarkup( format( "pluginxdoc.mojodescriptor.parameter.property.description", property ) );
715 w.writeMarkup( "<br/>" );
716 }
717
718 if ( StringUtils.isNotEmpty( parameter.getAlias() ) )
719 {
720 w.writeMarkup( format( "pluginxdoc.mojodescriptor.parameter.alias.description",
721 escapeXml( parameter.getAlias() ) ) );
722 }
723
724 w.endElement();
725 w.endElement();
726 }
727
728 w.endElement();
729 w.endElement();
730 }
731
732
733
734
735
736
737 private List<Parameter> getParametersByRequired( boolean required, List<Parameter> parameterList )
738 {
739 List<Parameter> list = new ArrayList<>();
740
741 for ( Parameter parameter : parameterList )
742 {
743 if ( parameter.isRequired() == required )
744 {
745 list.add( parameter );
746 }
747 }
748
749 return list;
750 }
751
752
753
754
755
756
757 private ResourceBundle getBundle()
758 {
759 return ResourceBundle.getBundle( "pluginxdoc", locale, getClass().getClassLoader() );
760 }
761
762
763
764
765
766
767 private String getString( String key )
768 {
769 return getBundle().getString( key );
770 }
771
772
773
774
775
776
777
778
779
780 private String format( String key, Object arg1 )
781 {
782 return format( key, new Object[]{ arg1 } );
783 }
784
785
786
787
788
789
790
791
792
793 private String format( String key, Object[] args )
794 {
795 String pattern = getString( key );
796
797 pattern = StringUtils.replace( pattern, "'", "''" );
798
799 MessageFormat messageFormat = new MessageFormat( "" );
800 messageFormat.setLocale( locale );
801 messageFormat.applyPattern( pattern );
802
803 return messageFormat.format( args );
804 }
805
806
807
808
809
810 private String escapeXml( String text )
811 {
812 if ( text != null )
813 {
814 text = text.replaceAll( "&", "&" );
815 text = text.replaceAll( "<", "<" );
816 text = text.replaceAll( ">", ">" );
817 text = text.replaceAll( "\"", """ );
818 text = text.replaceAll( "\'", "'" );
819 }
820 return text;
821 }
822
823 }