001package org.apache.maven.tools.plugin.generator;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import org.apache.maven.plugin.descriptor.MojoDescriptor;
023import org.apache.maven.plugin.descriptor.Parameter;
024import org.apache.maven.project.MavenProject;
025import org.apache.maven.tools.plugin.ExtendedMojoDescriptor;
026import org.apache.maven.tools.plugin.PluginToolsRequest;
027import org.codehaus.plexus.util.IOUtil;
028import org.codehaus.plexus.util.StringUtils;
029import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
030import org.codehaus.plexus.util.xml.XMLWriter;
031
032import java.io.File;
033import java.io.FileOutputStream;
034import java.io.IOException;
035import java.io.OutputStreamWriter;
036import java.io.PrintWriter;
037import java.io.Writer;
038import java.text.MessageFormat;
039import java.util.ArrayList;
040import java.util.Iterator;
041import java.util.List;
042import java.util.Locale;
043import java.util.ResourceBundle;
044
045/**
046 * Generate xdoc documentation for each mojo.
047 *
048 * @version $Id: PluginXdocGenerator.html 907040 2014-04-27 09:50:12Z hboutemy $
049 * @todo add example usage tag that can be shown in the doco
050 */
051public class PluginXdocGenerator
052    implements Generator
053{
054    /**
055     * locale
056     */
057    private final Locale locale;
058
059    /**
060     * project
061     */
062    private final MavenProject project;
063
064    /**
065     * Default constructor using <code>Locale.ENGLISH</code> as locale.
066     * Used only in test cases.
067     */
068    public PluginXdocGenerator()
069    {
070        this.project = null;
071        this.locale = Locale.ENGLISH;
072    }
073
074    /**
075     * Constructor using <code>Locale.ENGLISH</code> as locale.
076     *
077     * @param project not null Maven project.
078     */
079    public PluginXdocGenerator( MavenProject project )
080    {
081        this.project = project;
082        this.locale = Locale.ENGLISH;
083    }
084
085    /**
086     * @param project not null.
087     * @param locale  not null wanted locale.
088     */
089    public PluginXdocGenerator( MavenProject project, Locale locale )
090    {
091        this.project = project;
092        if ( locale == null )
093        {
094            this.locale = Locale.ENGLISH;
095        }
096        else
097        {
098            this.locale = locale;
099        }
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}