View Javadoc
1   package org.apache.maven.tools.plugin.generator;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
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   * Generate xdoc documentation for each mojo.
46   */
47  public class PluginXdocGenerator
48      implements Generator
49  {
50      /**
51       * locale
52       */
53      private final Locale locale;
54  
55      /**
56       * project
57       */
58      private final MavenProject project;
59  
60      /**
61       * Default constructor using <code>Locale.ENGLISH</code> as locale.
62       * Used only in test cases.
63       */
64      public PluginXdocGenerator()
65      {
66          this.project = null;
67          this.locale = Locale.ENGLISH;
68      }
69  
70      /**
71       * Constructor using <code>Locale.ENGLISH</code> as locale.
72       *
73       * @param project not null Maven project.
74       */
75      public PluginXdocGenerator( MavenProject project )
76      {
77          this.project = project;
78          this.locale = Locale.ENGLISH;
79      }
80  
81      /**
82       * @param project not null.
83       * @param locale  not null wanted locale.
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      * {@inheritDoc}
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      * @param mojoDescriptor       not null
126      * @param destinationDirectory not null
127      * @throws IOException if any
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      * @param mojo not null
146      * @param ext  not null
147      * @return the output file name
148      */
149     private String getMojoFilename( MojoDescriptor mojo, String ext )
150     {
151         return mojo.getGoal() + "-mojo." + ext;
152     }
153 
154     /**
155      * @param mojoDescriptor not null
156      * @param w              not null
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(); // title
175 
176         w.endElement(); // properties
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(); //p
193         w.startElement( "p" );
194         w.writeMarkup( mojoDescriptor.getPluginDescriptor().getGroupId() + ":"
195                            + mojoDescriptor.getPluginDescriptor().getArtifactId() + ":"
196                            + mojoDescriptor.getPluginDescriptor().getVersion() + ":" + mojoDescriptor.getGoal() );
197         w.endElement(); //p
198 
199         if ( StringUtils.isNotEmpty( mojoDescriptor.getDeprecated() ) )
200         {
201             w.startElement( "p" );
202             w.writeMarkup( getString( "pluginxdoc.mojodescriptor.deprecated" ) );
203             w.endElement(); // p
204             w.startElement( "div" );
205             w.writeMarkup( GeneratorUtils.makeHtmlValid( mojoDescriptor.getDeprecated() ) );
206             w.endElement(); // div
207         }
208 
209         w.startElement( "p" );
210         w.writeMarkup( getString( "pluginxdoc.description" ) );
211         w.endElement(); //p
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(); // div
222 
223         writeGoalAttributes( mojoDescriptor, w );
224 
225         writeGoalParameterTable( mojoDescriptor, w );
226 
227         w.endElement(); // section
228 
229         w.endElement(); // body
230 
231         w.endElement(); // document
232     }
233 
234     /**
235      * @param mojoDescriptor not null
236      * @param w              not null
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(); //p
246         }
247     }
248 
249     /**
250      * @param mojoDescriptor not null
251      * @param w              not null
252      */
253     private void writeGoalAttributes( MojoDescriptor mojoDescriptor, XMLWriter w )
254     {
255         w.startElement( "p" );
256         w.writeMarkup( getString( "pluginxdoc.mojodescriptor.attributes" ) );
257         w.endElement(); //p
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(); //li
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(); // li
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(); //li
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(); //li
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(); //li
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(); //li
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(); //li
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(); //li
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(); //li
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(); //li
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(); //li
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(); //li
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(); //li
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(); //li
384         }
385 
386         if ( addedUl )
387         {
388             w.endElement(); //ul
389         }
390     }
391 
392     /**
393      * @param mojoDescriptor not null
394      * @param w              not null
395      */
396     private void writeGoalParameterTable( MojoDescriptor mojoDescriptor, XMLWriter w )
397     {
398         List<Parameter> parameterList = mojoDescriptor.getParameters();
399 
400         // remove components and read-only parameters
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(); //p
417 
418             w.endElement();
419         }
420     }
421 
422     /**
423      * Filter parameters to only retain those which must be documented, ie not components nor readonly.
424      *
425      * @param parameterList not null
426      * @return the parameters list without components.
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      * @param mojoDescriptor not null
453      * @param parameterList  not null
454      * @param w              not null
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(); // div
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(); // div
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(); //ul
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             // expression="${xxx}" -> property="xxx"
579             return expression.substring( 2, expression.length() - 1 );
580         }
581         // no property can be extracted
582         return null;
583     }
584 
585     /**
586      * @param param not null
587      * @param value could be null
588      * @param w     not null
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(); //li
597         }
598     }
599 
600     /**
601      * @param mojoDescriptor not null
602      * @param parameterList  not null
603      * @param w              not null
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      * @param mojoDescriptor not null
624      * @param title          not null
625      * @param parameterList  not null
626      * @param w              not null
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(); //th
641         w.startElement( "th" );
642         w.writeText( getString( "pluginxdoc.mojodescriptor.parameter.type" ) );
643         w.endElement(); //th
644         w.startElement( "th" );
645         w.writeText( getString( "pluginxdoc.mojodescriptor.parameter.since" ) );
646         w.endElement(); //th
647         w.startElement( "th" );
648         w.writeText( getString( "pluginxdoc.mojodescriptor.parameter.description" ) );
649         w.endElement(); //th
650         w.endElement(); //tr
651 
652         for ( Parameter parameter : parameterList )
653         {
654             w.startElement( "tr" );
655 
656             // name
657             w.startElement( "td" );
658             w.writeMarkup( format( "pluginxdoc.mojodescriptor.parameter.name_link", parameter.getName() ) );
659             w.endElement(); //td
660 
661             //type
662             w.startElement( "td" );
663             int index = parameter.getType().lastIndexOf( "." );
664             w.writeMarkup( "<code>" + parameter.getType().substring( index + 1 ) + "</code>" );
665             w.endElement(); //td
666 
667             // since
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(); //td
685 
686             // description
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(); //td
725             w.endElement(); //tr
726         }
727 
728         w.endElement(); //table
729         w.endElement(); //section
730     }
731 
732     /**
733      * @param required      <code>true</code> for required parameters, <code>false</code> otherwise.
734      * @param parameterList not null
735      * @return list of parameters depending the value of <code>required</code>
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      * Gets the resource bundle for the <code>locale</code> instance variable.
754      *
755      * @return The resource bundle for the <code>locale</code> instance variable.
756      */
757     private ResourceBundle getBundle()
758     {
759         return ResourceBundle.getBundle( "pluginxdoc", locale, getClass().getClassLoader() );
760     }
761 
762     /**
763      * @param key not null
764      * @return Localized, text identified by <code>key</code>.
765      * @see #getBundle()
766      */
767     private String getString( String key )
768     {
769         return getBundle().getString( key );
770     }
771 
772     /**
773      * Convenience method.
774      *
775      * @param key  not null
776      * @param arg1 not null
777      * @return Localized, formatted text identified by <code>key</code>.
778      * @see #format(String, Object[])
779      */
780     private String format( String key, Object arg1 )
781     {
782         return format( key, new Object[]{ arg1 } );
783     }
784 
785     /**
786      * Looks up the value for <code>key</code> in the <code>ResourceBundle</code>,
787      * then formats that value for the specified <code>Locale</code> using <code>args</code>.
788      *
789      * @param key  not null
790      * @param args not null
791      * @return Localized, formatted text identified by <code>key</code>.
792      */
793     private String format( String key, Object[] args )
794     {
795         String pattern = getString( key );
796         // we don't need quoting so spare us the confusion in the resource bundle to double them up in some keys
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      * @param text the string to escape
808      * @return A string escaped with XML entities
809      */
810     private String escapeXml( String text )
811     {
812         if ( text != null )
813         {
814             text = text.replaceAll( "&", "&amp;" );
815             text = text.replaceAll( "<", "&lt;" );
816             text = text.replaceAll( ">", "&gt;" );
817             text = text.replaceAll( "\"", "&quot;" );
818             text = text.replaceAll( "\'", "&apos;" );
819         }
820         return text;
821     }
822 
823 }