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