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