001    package 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    
022    import org.apache.maven.plugin.descriptor.MojoDescriptor;
023    import org.apache.maven.plugin.descriptor.Parameter;
024    import org.apache.maven.project.MavenProject;
025    import org.apache.maven.tools.plugin.ExtendedMojoDescriptor;
026    import org.apache.maven.tools.plugin.PluginToolsRequest;
027    import org.codehaus.plexus.util.IOUtil;
028    import org.codehaus.plexus.util.StringUtils;
029    import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
030    import org.codehaus.plexus.util.xml.XMLWriter;
031    
032    import java.io.File;
033    import java.io.FileOutputStream;
034    import java.io.IOException;
035    import java.io.OutputStreamWriter;
036    import java.io.PrintWriter;
037    import java.io.Writer;
038    import java.text.MessageFormat;
039    import java.util.ArrayList;
040    import java.util.Iterator;
041    import java.util.List;
042    import java.util.Locale;
043    import java.util.ResourceBundle;
044    
045    /**
046     * Generate xdoc documentation for each mojo.
047     *
048     * @version $Id: PluginXdocGenerator.java 1341236 2012-05-21 22:37:15Z hboutemy $
049     * @todo add example usage tag that can be shown in the doco
050     */
051    public 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    }