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.DuplicateMojoDescriptorException;
023    import org.apache.maven.plugin.descriptor.MojoDescriptor;
024    import org.apache.maven.plugin.descriptor.Parameter;
025    import org.apache.maven.plugin.descriptor.PluginDescriptor;
026    import org.apache.maven.plugin.descriptor.Requirement;
027    import org.apache.maven.project.MavenProject;
028    import org.apache.maven.tools.plugin.ExtendedMojoDescriptor;
029    import org.apache.maven.tools.plugin.PluginToolsRequest;
030    import org.apache.maven.tools.plugin.util.PluginUtils;
031    import org.codehaus.plexus.util.IOUtil;
032    import org.codehaus.plexus.util.StringUtils;
033    import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
034    import org.codehaus.plexus.util.xml.XMLWriter;
035    
036    import java.io.File;
037    import java.io.FileOutputStream;
038    import java.io.IOException;
039    import java.io.OutputStreamWriter;
040    import java.io.Writer;
041    import java.text.SimpleDateFormat;
042    import java.util.Date;
043    import java.util.LinkedHashMap;
044    import java.util.LinkedHashSet;
045    import java.util.List;
046    import java.util.Map;
047    import java.util.Set;
048    
049    /**
050     * Generate a <a href="/ref/current/maven-plugin-api/plugin.html">Maven Plugin Descriptor XML file</a> and
051     * corresponding help content.
052     *
053     * @version $Id: PluginDescriptorGenerator.java 1354239 2012-06-26 21:19:06Z hboutemy $
054     * @todo add example usage tag that can be shown in the doco
055     * @todo need to add validation directives so that systems embedding maven2 can
056     * get validation directives to help users in IDEs.
057     */
058    public class PluginDescriptorGenerator
059        implements Generator
060    {
061    
062        /**
063         * {@inheritDoc}
064         */
065        public void execute( File destinationDirectory, PluginToolsRequest request )
066            throws GeneratorException
067        {
068            // eventually rewrite help mojo class to match actual package name
069            PluginHelpGenerator.rewriteHelpMojo( request );
070    
071            try
072            {
073                // write complete plugin.xml descriptor
074                File f = new File( destinationDirectory, "plugin.xml" );
075                writeDescriptor( f, request, false );
076    
077                // write plugin-help.xml help-descriptor
078                MavenProject mavenProject = request.getProject();
079                String pluginHelpFilePath =
080                    "META-INF/maven/" + mavenProject.getGroupId() + "/" + mavenProject.getArtifactId() + "/plugin-help.xml";
081    
082                f = new File( request.getProject().getBuild().getOutputDirectory(), pluginHelpFilePath );
083                writeDescriptor( f, request, true );
084            }
085            catch ( IOException e )
086            {
087                throw new GeneratorException( e.getMessage(), e );
088            }
089            catch ( DuplicateMojoDescriptorException e )
090            {
091                throw new GeneratorException( e.getMessage(), e );
092            }
093        }
094    
095        private String getVersion()
096        {
097            Package p = this.getClass().getPackage();
098            String version = ( p == null ) ? null : p.getSpecificationVersion();
099            return ( version == null ) ? "SNAPSHOT" : version;
100        }
101    
102        public void writeDescriptor( File destinationFile, PluginToolsRequest request, boolean helpDescriptor )
103            throws IOException, DuplicateMojoDescriptorException
104        {
105            PluginDescriptor pluginDescriptor = request.getPluginDescriptor();
106    
107            if ( destinationFile.exists() )
108            {
109                destinationFile.delete();
110            }
111            else
112            {
113                if ( !destinationFile.getParentFile().exists() )
114                {
115                    destinationFile.getParentFile().mkdirs();
116                }
117            }
118    
119            String encoding = "UTF-8";
120    
121            Writer writer = null;
122            try
123            {
124                writer = new OutputStreamWriter( new FileOutputStream( destinationFile ), encoding );
125    
126                XMLWriter w = new PrettyPrintXMLWriter( writer, encoding, null );
127    
128                w.writeMarkup( "\n<!-- Generated by maven-plugin-tools " + getVersion() + " on " + new SimpleDateFormat(
129                    "yyyy-MM-dd" ).format( new Date() ) + " -->\n\n" );
130    
131                w.startElement( "plugin" );
132    
133                GeneratorUtils.element( w, "name", pluginDescriptor.getName() );
134    
135                GeneratorUtils.element( w, "description", pluginDescriptor.getDescription(), helpDescriptor );
136    
137                GeneratorUtils.element( w, "groupId", pluginDescriptor.getGroupId() );
138    
139                GeneratorUtils.element( w, "artifactId", pluginDescriptor.getArtifactId() );
140    
141                GeneratorUtils.element( w, "version", pluginDescriptor.getVersion() );
142    
143                GeneratorUtils.element( w, "goalPrefix", pluginDescriptor.getGoalPrefix() );
144    
145                if ( !helpDescriptor )
146                {
147                    GeneratorUtils.element( w, "isolatedRealm", String.valueOf( pluginDescriptor.isIsolatedRealm() ) );
148    
149                    GeneratorUtils.element( w, "inheritedByDefault",
150                                            String.valueOf( pluginDescriptor.isInheritedByDefault() ) );
151                }
152    
153                w.startElement( "mojos" );
154    
155                if ( pluginDescriptor.getMojos() != null )
156                {
157                    @SuppressWarnings( "unchecked" ) List<MojoDescriptor> descriptors = pluginDescriptor.getMojos();
158    
159                    if ( helpDescriptor )
160                    {
161                        PluginUtils.sortMojos( descriptors );
162                    }
163    
164                    for ( MojoDescriptor descriptor : descriptors )
165                    {
166                        processMojoDescriptor( descriptor, w, helpDescriptor );
167                    }
168                }
169    
170                w.endElement();
171    
172                if ( !helpDescriptor )
173                {
174                    GeneratorUtils.writeDependencies( w, pluginDescriptor );
175                }
176    
177                w.endElement();
178    
179                writer.flush();
180    
181            }
182            finally
183            {
184                IOUtil.close( writer );
185            }
186        }
187    
188        protected void processMojoDescriptor( MojoDescriptor mojoDescriptor, XMLWriter w )
189        {
190            processMojoDescriptor( mojoDescriptor, w, false );
191        }
192    
193        /**
194         * @param mojoDescriptor not null
195         * @param w              not null
196         * @param helpDescriptor will clean html content from description fields
197         */
198        protected void processMojoDescriptor( MojoDescriptor mojoDescriptor, XMLWriter w, boolean helpDescriptor )
199        {
200            w.startElement( "mojo" );
201    
202            // ----------------------------------------------------------------------
203            //
204            // ----------------------------------------------------------------------
205    
206            w.startElement( "goal" );
207            w.writeText( mojoDescriptor.getGoal() );
208            w.endElement();
209    
210            // ----------------------------------------------------------------------
211            //
212            // ----------------------------------------------------------------------
213    
214            String description = mojoDescriptor.getDescription();
215    
216            if ( description != null )
217            {
218                w.startElement( "description" );
219                if ( helpDescriptor )
220                {
221                    w.writeText( GeneratorUtils.toText( mojoDescriptor.getDescription() ) );
222                }
223                else
224                {
225                    w.writeText( mojoDescriptor.getDescription() );
226                }
227                w.endElement();
228            }
229    
230            // ----------------------------------------------------------------------
231            //
232            // ----------------------------------------------------------------------
233    
234            if ( StringUtils.isNotEmpty( mojoDescriptor.isDependencyResolutionRequired() ) )
235            {
236                GeneratorUtils.element( w, "requiresDependencyResolution",
237                                        mojoDescriptor.isDependencyResolutionRequired() );
238            }
239    
240            // ----------------------------------------------------------------------
241            //
242            // ----------------------------------------------------------------------
243    
244            GeneratorUtils.element( w, "requiresDirectInvocation",
245                                    String.valueOf( mojoDescriptor.isDirectInvocationOnly() ) );
246    
247            // ----------------------------------------------------------------------
248            //
249            // ----------------------------------------------------------------------
250    
251            GeneratorUtils.element( w, "requiresProject", String.valueOf( mojoDescriptor.isProjectRequired() ) );
252    
253            // ----------------------------------------------------------------------
254            //
255            // ----------------------------------------------------------------------
256    
257            GeneratorUtils.element( w, "requiresReports", String.valueOf( mojoDescriptor.isRequiresReports() ) );
258    
259            // ----------------------------------------------------------------------
260            //
261            // ----------------------------------------------------------------------
262    
263            GeneratorUtils.element( w, "aggregator", String.valueOf( mojoDescriptor.isAggregator() ) );
264    
265            // ----------------------------------------------------------------------
266            //
267            // ----------------------------------------------------------------------
268    
269            GeneratorUtils.element( w, "requiresOnline", String.valueOf( mojoDescriptor.isOnlineRequired() ) );
270    
271            // ----------------------------------------------------------------------
272            //
273            // ----------------------------------------------------------------------
274    
275            GeneratorUtils.element( w, "inheritedByDefault", String.valueOf( mojoDescriptor.isInheritedByDefault() ) );
276    
277            // ----------------------------------------------------------------------
278            //
279            // ----------------------------------------------------------------------
280    
281            if ( StringUtils.isNotEmpty( mojoDescriptor.getPhase() ) )
282            {
283                GeneratorUtils.element( w, "phase", mojoDescriptor.getPhase() );
284            }
285    
286            // ----------------------------------------------------------------------
287            //
288            // ----------------------------------------------------------------------
289    
290            if ( StringUtils.isNotEmpty( mojoDescriptor.getExecutePhase() ) )
291            {
292                GeneratorUtils.element( w, "executePhase", mojoDescriptor.getExecutePhase() );
293            }
294    
295            if ( StringUtils.isNotEmpty( mojoDescriptor.getExecuteGoal() ) )
296            {
297                GeneratorUtils.element( w, "executeGoal", mojoDescriptor.getExecuteGoal() );
298            }
299    
300            if ( StringUtils.isNotEmpty( mojoDescriptor.getExecuteLifecycle() ) )
301            {
302                GeneratorUtils.element( w, "executeLifecycle", mojoDescriptor.getExecuteLifecycle() );
303            }
304    
305            // ----------------------------------------------------------------------
306            //
307            // ----------------------------------------------------------------------
308    
309            w.startElement( "implementation" );
310            w.writeText( mojoDescriptor.getImplementation() );
311            w.endElement();
312    
313            // ----------------------------------------------------------------------
314            //
315            // ----------------------------------------------------------------------
316    
317            w.startElement( "language" );
318            w.writeText( mojoDescriptor.getLanguage() );
319            w.endElement();
320    
321            // ----------------------------------------------------------------------
322            //
323            // ----------------------------------------------------------------------
324    
325            if ( StringUtils.isNotEmpty( mojoDescriptor.getComponentConfigurator() ) )
326            {
327                w.startElement( "configurator" );
328                w.writeText( mojoDescriptor.getComponentConfigurator() );
329                w.endElement();
330            }
331    
332            // ----------------------------------------------------------------------
333            //
334            // ----------------------------------------------------------------------
335    
336            if ( StringUtils.isNotEmpty( mojoDescriptor.getComponentComposer() ) )
337            {
338                w.startElement( "composer" );
339                w.writeText( mojoDescriptor.getComponentComposer() );
340                w.endElement();
341            }
342    
343            // ----------------------------------------------------------------------
344            //
345            // ----------------------------------------------------------------------
346    
347            w.startElement( "instantiationStrategy" );
348            w.writeText( mojoDescriptor.getInstantiationStrategy() );
349            w.endElement();
350    
351            // ----------------------------------------------------------------------
352            // Strategy for handling repeated reference to mojo in
353            // the calculated (decorated, resolved) execution stack
354            // ----------------------------------------------------------------------
355            w.startElement( "executionStrategy" );
356            w.writeText( mojoDescriptor.getExecutionStrategy() );
357            w.endElement();
358    
359            // ----------------------------------------------------------------------
360            //
361            // ----------------------------------------------------------------------
362    
363            if ( mojoDescriptor.getSince() != null )
364            {
365                w.startElement( "since" );
366    
367                if ( StringUtils.isEmpty( mojoDescriptor.getSince() ) )
368                {
369                    w.writeText( "No version given" );
370                }
371                else
372                {
373                    w.writeText( mojoDescriptor.getSince() );
374                }
375    
376                w.endElement();
377            }
378    
379            // ----------------------------------------------------------------------
380            //
381            // ----------------------------------------------------------------------
382    
383            if ( mojoDescriptor.getDeprecated() != null )
384            {
385                w.startElement( "deprecated" );
386    
387                if ( StringUtils.isEmpty( mojoDescriptor.getDeprecated() ) )
388                {
389                    w.writeText( "No reason given" );
390                }
391                else
392                {
393                    w.writeText( mojoDescriptor.getDeprecated() );
394                }
395    
396                w.endElement();
397            }
398    
399            // ----------------------------------------------------------------------
400            // Extended (3.0) descriptor
401            // ----------------------------------------------------------------------
402    
403            if ( mojoDescriptor instanceof ExtendedMojoDescriptor )
404            {
405                ExtendedMojoDescriptor extendedMojoDescriptor = (ExtendedMojoDescriptor) mojoDescriptor;
406                if ( extendedMojoDescriptor.getDependencyCollectionRequired() != null )
407                {
408                    GeneratorUtils.element( w, "requiresDependencyCollection",
409                                            extendedMojoDescriptor.getDependencyCollectionRequired() );
410                }
411    
412                GeneratorUtils.element( w, "threadSafe", String.valueOf( extendedMojoDescriptor.isThreadSafe() ) );
413            }
414    
415            // ----------------------------------------------------------------------
416            // Parameters
417            // ----------------------------------------------------------------------
418    
419            @SuppressWarnings( "unchecked" ) List<Parameter> parameters = mojoDescriptor.getParameters();
420    
421            w.startElement( "parameters" );
422    
423            Map<String, Requirement> requirements = new LinkedHashMap<String, Requirement>();
424    
425            Set<Parameter> configuration = new LinkedHashSet<Parameter>();
426    
427            if ( parameters != null )
428            {
429                if ( helpDescriptor )
430                {
431                    PluginUtils.sortMojoParameters( parameters );
432                }
433    
434                for ( Parameter parameter : parameters )
435                {
436                    String expression = getExpression( parameter );
437    
438                    if ( StringUtils.isNotEmpty( expression ) && expression.startsWith( "${component." ) )
439                    {
440                        // treat it as a component...a requirement, in other words.
441    
442                        // remove "component." plus expression delimiters
443                        String role = expression.substring( "${component.".length(), expression.length() - 1 );
444    
445                        String roleHint = null;
446    
447                        int posRoleHintSeparator = role.indexOf( "#" );
448                        if ( posRoleHintSeparator > 0 )
449                        {
450                            roleHint = role.substring( posRoleHintSeparator + 1 );
451    
452                            role = role.substring( 0, posRoleHintSeparator );
453                        }
454    
455                        // TODO: remove deprecated expression
456                        requirements.put( parameter.getName(), new Requirement( role, roleHint ) );
457                    }
458                    else if ( parameter.getRequirement() != null )
459                    {
460                        requirements.put( parameter.getName(), parameter.getRequirement() );
461                    }
462                    else if ( !helpDescriptor || parameter.isEditable() ) // don't show readonly parameters in help
463                    {
464                        // treat it as a normal parameter.
465    
466                        w.startElement( "parameter" );
467    
468                        GeneratorUtils.element( w, "name", parameter.getName() );
469    
470                        if ( parameter.getAlias() != null )
471                        {
472                            GeneratorUtils.element( w, "alias", parameter.getAlias() );
473                        }
474    
475                        GeneratorUtils.element( w, "type", parameter.getType() );
476    
477                        if ( parameter.getSince() != null )
478                        {
479                            w.startElement( "since" );
480    
481                            if ( StringUtils.isEmpty( parameter.getSince() ) )
482                            {
483                                w.writeText( "No version given" );
484                            }
485                            else
486                            {
487                                w.writeText( parameter.getSince() );
488                            }
489    
490                            w.endElement();
491                        }
492    
493                        if ( parameter.getDeprecated() != null )
494                        {
495                            if ( StringUtils.isEmpty( parameter.getDeprecated() ) )
496                            {
497                                GeneratorUtils.element( w, "deprecated", "No reason given" );
498                            }
499                            else
500                            {
501                                GeneratorUtils.element( w, "deprecated", parameter.getDeprecated() );
502                            }
503                        }
504    
505                        if ( parameter.getImplementation() != null )
506                        {
507                            GeneratorUtils.element( w, "implementation", parameter.getImplementation() );
508                        }
509    
510                        GeneratorUtils.element( w, "required", Boolean.toString( parameter.isRequired() ) );
511    
512                        GeneratorUtils.element( w, "editable", Boolean.toString( parameter.isEditable() ) );
513    
514                        GeneratorUtils.element( w, "description", parameter.getDescription(), helpDescriptor );
515    
516                        if ( StringUtils.isNotEmpty( parameter.getDefaultValue() ) || StringUtils.isNotEmpty(
517                            parameter.getExpression() ) )
518                        {
519                            configuration.add( parameter );
520                        }
521    
522                        w.endElement();
523                    }
524    
525                }
526            }
527    
528            w.endElement();
529    
530            // ----------------------------------------------------------------------
531            // Configuration
532            // ----------------------------------------------------------------------
533    
534            if ( !configuration.isEmpty() )
535            {
536                w.startElement( "configuration" );
537    
538                for ( Parameter parameter : configuration )
539                {
540                    if ( helpDescriptor && !parameter.isEditable() )
541                    {
542                        // don't show readonly parameters in help
543                        continue;
544                    }
545    
546                    w.startElement( parameter.getName() );
547    
548                    String type = parameter.getType();
549                    if ( StringUtils.isNotEmpty( type ) )
550                    {
551                        w.addAttribute( "implementation", type );
552                    }
553    
554                    if ( parameter.getDefaultValue() != null )
555                    {
556                        w.addAttribute( "default-value", parameter.getDefaultValue() );
557                    }
558    
559                    if ( StringUtils.isNotEmpty( parameter.getExpression() ) )
560                    {
561                        w.writeText( parameter.getExpression() );
562                    }
563    
564                    w.endElement();
565                }
566    
567                w.endElement();
568            }
569    
570            // ----------------------------------------------------------------------
571            // Requirements
572            // ----------------------------------------------------------------------
573    
574            if ( !requirements.isEmpty() && !helpDescriptor )
575            {
576                w.startElement( "requirements" );
577    
578                for ( Map.Entry<String, Requirement> entry : requirements.entrySet() )
579                {
580                    String key = entry.getKey();
581                    Requirement requirement = entry.getValue();
582    
583                    w.startElement( "requirement" );
584    
585                    GeneratorUtils.element( w, "role", requirement.getRole() );
586    
587                    if ( StringUtils.isNotEmpty( requirement.getRoleHint() ) )
588                    {
589                        GeneratorUtils.element( w, "role-hint", requirement.getRoleHint() );
590                    }
591    
592                    GeneratorUtils.element( w, "field-name", key );
593    
594                    w.endElement();
595                }
596    
597                w.endElement();
598            }
599    
600            w.endElement();
601        }
602    
603        /**
604         * Get the expression value, eventually surrounding it with <code>${ }</code>.
605         *
606         * @param parameter the parameter
607         * @return the expression value
608         */
609        private String getExpression( Parameter parameter )
610        {
611            String expression = parameter.getExpression();
612            if ( StringUtils.isNotBlank( expression ) && !expression.contains( "${" ) )
613            {
614                expression = "${" + expression.trim() + "}";
615                parameter.setExpression( expression );
616            }
617            return expression;
618        }
619    }