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