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 934731 2015-01-01 22:17:21Z hboutemy $
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             if ( addedUl )
548             {
549                 w.endElement(); //ul
550             }
551 
552             if ( parameters.hasNext() )
553             {
554                 w.writeMarkup( "<hr/>" );
555             }
556         }
557 
558         w.endElement();
559     }
560 
561     private boolean addUl( XMLWriter w, boolean addedUl, String content )
562     {
563         if ( StringUtils.isNotEmpty( content ) )
564         {
565             return addUl( w, addedUl );
566         }
567         return addedUl;
568     }
569 
570     private boolean addUl( XMLWriter w, boolean addedUl )
571     {
572         if ( !addedUl )
573         {
574             w.startElement( "ul" );
575             addedUl = true;
576         }
577         return addedUl;
578     }
579 
580     private String getPropertyFromExpression( String expression )
581     {
582         if ( StringUtils.isNotEmpty( expression ) && expression.startsWith( "${" ) && expression.endsWith( "}" )
583             && !expression.substring( 2 ).contains( "${" ) )
584         {
585             // expression="${xxx}" -> property="xxx"
586             return expression.substring( 2, expression.length() - 1 );
587         }
588         // no property can be extracted
589         return null;
590     }
591     
592     /**
593      * @param param not null
594      * @param value could be null
595      * @param w     not null
596      */
597     private void writeDetail( String param, String value, XMLWriter w )
598     {
599         if ( StringUtils.isNotEmpty( value ) )
600         {
601             w.startElement( "li" );
602             w.writeMarkup( format( "pluginxdoc.detail", new String[]{ param, value } ) );
603             w.endElement(); //li
604         }
605     }
606 
607     /**
608      * @param mojoDescriptor not null
609      * @param parameterList  not null
610      * @param w              not null
611      */
612     private void writeParameterSummary( MojoDescriptor mojoDescriptor, List<Parameter> parameterList, XMLWriter w )
613     {
614         List<Parameter> requiredParams = getParametersByRequired( true, parameterList );
615         if ( requiredParams.size() > 0 )
616         {
617             writeParameterList( mojoDescriptor, getString( "pluginxdoc.mojodescriptor.requiredParameters" ),
618                                 requiredParams, w );
619         }
620 
621         List<Parameter> optionalParams = getParametersByRequired( false, parameterList );
622         if ( optionalParams.size() > 0 )
623         {
624             writeParameterList( mojoDescriptor, getString( "pluginxdoc.mojodescriptor.optionalParameters" ),
625                                 optionalParams, w );
626         }
627     }
628 
629     /**
630      * @param mojoDescriptor not null
631      * @param title          not null
632      * @param parameterList  not null
633      * @param w              not null
634      */
635     private void writeParameterList( MojoDescriptor mojoDescriptor, String title, List<Parameter> parameterList,
636                                      XMLWriter w )
637     {
638         w.startElement( "subsection" );
639         w.addAttribute( "name", title );
640 
641         w.startElement( "table" );
642         w.addAttribute( "border", "0" );
643 
644         w.startElement( "tr" );
645         w.startElement( "th" );
646         w.writeText( getString( "pluginxdoc.mojodescriptor.parameter.name" ) );
647         w.endElement(); //th
648         w.startElement( "th" );
649         w.writeText( getString( "pluginxdoc.mojodescriptor.parameter.type" ) );
650         w.endElement(); //th
651         w.startElement( "th" );
652         w.writeText( getString( "pluginxdoc.mojodescriptor.parameter.since" ) );
653         w.endElement(); //th
654         w.startElement( "th" );
655         w.writeText( getString( "pluginxdoc.mojodescriptor.parameter.description" ) );
656         w.endElement(); //th
657         w.endElement(); //tr
658 
659         for ( Parameter parameter : parameterList )
660         {
661             w.startElement( "tr" );
662 
663             // name
664             w.startElement( "td" );
665             w.writeMarkup( format( "pluginxdoc.mojodescriptor.parameter.name_link", parameter.getName() ) );
666             w.endElement(); //td
667 
668             //type
669             w.startElement( "td" );
670             int index = parameter.getType().lastIndexOf( "." );
671             w.writeMarkup( "<code>" + parameter.getType().substring( index + 1 ) + "</code>" );
672             w.endElement(); //td
673 
674             // since
675             w.startElement( "td" );
676             if ( StringUtils.isNotEmpty( parameter.getSince() ) )
677             {
678                 w.writeMarkup( "<code>" + parameter.getSince() + "</code>" );
679             }
680             else
681             {
682                 if ( StringUtils.isNotEmpty( mojoDescriptor.getSince() ) )
683                 {
684                     w.writeMarkup( "<code>" + mojoDescriptor.getSince() + "</code>" );
685                 }
686                 else
687                 {
688                     w.writeMarkup( "<code>-</code>" );
689                 }
690             }
691             w.endElement(); //td
692 
693             // description
694             w.startElement( "td" );
695             String description;
696             if ( StringUtils.isNotEmpty( parameter.getDeprecated() ) )
697             {
698                 description = format( "pluginxdoc.mojodescriptor.parameter.deprecated",
699                                       GeneratorUtils.makeHtmlValid( parameter.getDeprecated() ) );
700             }
701             else if ( StringUtils.isNotEmpty( parameter.getDescription() ) )
702             {
703                 description = GeneratorUtils.makeHtmlValid( parameter.getDescription() );
704             }
705             else
706             {
707                 description = getString( "pluginxdoc.nodescription" );
708             }
709             w.writeMarkup( description + "<br/>" );
710 
711             if ( StringUtils.isNotEmpty( parameter.getDefaultValue() ) )
712             {
713                 w.writeMarkup( format( "pluginxdoc.mojodescriptor.parameter.defaultValue",
714                                        escapeXml( parameter.getDefaultValue() ) ) );
715                 w.writeMarkup( "<br/>" );
716             }
717 
718             String property = getPropertyFromExpression( parameter.getExpression() );
719             if ( property != null )
720             {
721                 w.writeMarkup( format( "pluginxdoc.mojodescriptor.parameter.property.description", property ) );
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<Parameter>();
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 }