View Javadoc
1   package org.apache.maven.tools.plugin.generator;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.FileOutputStream;
24  import java.io.IOException;
25  import java.io.OutputStreamWriter;
26  import java.io.Writer;
27  import java.util.LinkedHashMap;
28  import java.util.LinkedHashSet;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Set;
32  import org.apache.maven.plugin.descriptor.DuplicateMojoDescriptorException;
33  import org.apache.maven.plugin.descriptor.MojoDescriptor;
34  import org.apache.maven.plugin.descriptor.Parameter;
35  import org.apache.maven.plugin.descriptor.PluginDescriptor;
36  import org.apache.maven.plugin.descriptor.Requirement;
37  import org.apache.maven.plugin.logging.Log;
38  import org.apache.maven.project.MavenProject;
39  import org.apache.maven.tools.plugin.ExtendedMojoDescriptor;
40  import org.apache.maven.tools.plugin.PluginToolsRequest;
41  import org.apache.maven.tools.plugin.util.PluginUtils;
42  import org.codehaus.plexus.util.IOUtil;
43  import org.codehaus.plexus.util.StringUtils;
44  import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
45  import org.codehaus.plexus.util.xml.XMLWriter;
46  
47  /**
48   * Generate a <a href="/ref/current/maven-plugin-api/plugin.html">Maven Plugin Descriptor XML file</a> and
49   * corresponding <code>plugin-help.xml</code> help content for {@link PluginHelpGenerator}.
50   *
51   * @version $Id: PluginDescriptorGenerator.html 1024032 2018-01-19 18:16:30Z hboutemy $
52   */
53  public class PluginDescriptorGenerator
54      implements Generator
55  {
56  
57      private final Log log;
58  
59      public PluginDescriptorGenerator( Log log )
60      {
61          this.log = log;
62      }
63  
64      /**
65       * {@inheritDoc}
66       */
67      public void execute( File destinationDirectory, PluginToolsRequest request )
68          throws GeneratorException
69      {
70          // eventually rewrite help mojo class to match actual package name
71          PluginHelpGenerator.rewriteHelpMojo( request, log );
72  
73          try
74          {
75              // write complete plugin.xml descriptor
76              File f = new File( destinationDirectory, "plugin.xml" );
77              writeDescriptor( f, request, false );
78  
79              // write plugin-help.xml help-descriptor
80              MavenProject mavenProject = request.getProject();
81  
82              f = new File( mavenProject.getBuild().getOutputDirectory(),
83                            PluginHelpGenerator.getPluginHelpPath( mavenProject ) );
84  
85              writeDescriptor( f, request, true );
86          }
87          catch ( IOException e )
88          {
89              throw new GeneratorException( e.getMessage(), e );
90          }
91          catch ( DuplicateMojoDescriptorException e )
92          {
93              throw new GeneratorException( e.getMessage(), e );
94          }
95      }
96  
97      private String getVersion()
98      {
99          Package p = this.getClass().getPackage();
100         String version = ( p == null ) ? null : p.getSpecificationVersion();
101         return ( version == null ) ? "SNAPSHOT" : version;
102     }
103 
104     public void writeDescriptor( File destinationFile, PluginToolsRequest request, boolean helpDescriptor )
105         throws IOException, DuplicateMojoDescriptorException
106     {
107         PluginDescriptor pluginDescriptor = request.getPluginDescriptor();
108 
109         if ( destinationFile.exists() )
110         {
111             destinationFile.delete();
112         }
113         else
114         {
115             if ( !destinationFile.getParentFile().exists() )
116             {
117                 destinationFile.getParentFile().mkdirs();
118             }
119         }
120 
121         String encoding = "UTF-8";
122 
123         Writer writer = null;
124         try
125         {
126             writer = new OutputStreamWriter( new FileOutputStream( destinationFile ), encoding );
127 
128             XMLWriter w = new PrettyPrintXMLWriter( writer, encoding, null );
129 
130             w.writeMarkup( "\n<!-- Generated by maven-plugin-tools " + getVersion() + " -->\n\n" );
131 
132             w.startElement( "plugin" );
133 
134             GeneratorUtils.element( w, "name", pluginDescriptor.getName() );
135 
136             GeneratorUtils.element( w, "description", pluginDescriptor.getDescription(), helpDescriptor );
137 
138             GeneratorUtils.element( w, "groupId", pluginDescriptor.getGroupId() );
139 
140             GeneratorUtils.element( w, "artifactId", pluginDescriptor.getArtifactId() );
141 
142             GeneratorUtils.element( w, "version", pluginDescriptor.getVersion() );
143 
144             GeneratorUtils.element( w, "goalPrefix", pluginDescriptor.getGoalPrefix() );
145 
146             if ( !helpDescriptor )
147             {
148                 GeneratorUtils.element( w, "isolatedRealm", String.valueOf( pluginDescriptor.isIsolatedRealm() ) );
149 
150                 GeneratorUtils.element( w, "inheritedByDefault",
151                                         String.valueOf( pluginDescriptor.isInheritedByDefault() ) );
152             }
153 
154             w.startElement( "mojos" );
155 
156             if ( pluginDescriptor.getMojos() != null )
157             {
158                 @SuppressWarnings( "unchecked" ) List<MojoDescriptor> descriptors = pluginDescriptor.getMojos();
159 
160                 PluginUtils.sortMojos( descriptors );
161 
162                 for ( MojoDescriptor descriptor : descriptors )
163                 {
164                     processMojoDescriptor( descriptor, w, helpDescriptor );
165                 }
166             }
167 
168             w.endElement();
169 
170             if ( !helpDescriptor )
171             {
172                 GeneratorUtils.writeDependencies( w, pluginDescriptor );
173             }
174 
175             w.endElement();
176 
177             writer.flush();
178 
179         }
180         finally
181         {
182             IOUtil.close( writer );
183         }
184     }
185 
186     protected void processMojoDescriptor( MojoDescriptor mojoDescriptor, XMLWriter w )
187     {
188         processMojoDescriptor( mojoDescriptor, w, false );
189     }
190 
191     /**
192      * @param mojoDescriptor not null
193      * @param w              not null
194      * @param helpDescriptor will clean html content from description fields
195      */
196     protected void processMojoDescriptor( MojoDescriptor mojoDescriptor, XMLWriter w, boolean helpDescriptor )
197     {
198         w.startElement( "mojo" );
199 
200         // ----------------------------------------------------------------------
201         //
202         // ----------------------------------------------------------------------
203 
204         w.startElement( "goal" );
205         w.writeText( mojoDescriptor.getGoal() );
206         w.endElement();
207 
208         // ----------------------------------------------------------------------
209         //
210         // ----------------------------------------------------------------------
211 
212         String description = mojoDescriptor.getDescription();
213 
214         if ( StringUtils.isNotEmpty( description ) )
215         {
216             w.startElement( "description" );
217             if ( helpDescriptor )
218             {
219                 w.writeText( GeneratorUtils.toText( mojoDescriptor.getDescription() ) );
220             }
221             else
222             {
223                 w.writeText( mojoDescriptor.getDescription() );
224             }
225             w.endElement();
226         }
227 
228         // ----------------------------------------------------------------------
229         //
230         // ----------------------------------------------------------------------
231 
232         if ( StringUtils.isNotEmpty( mojoDescriptor.isDependencyResolutionRequired() ) )
233         {
234             GeneratorUtils.element( w, "requiresDependencyResolution",
235                                     mojoDescriptor.isDependencyResolutionRequired() );
236         }
237 
238         // ----------------------------------------------------------------------
239         //
240         // ----------------------------------------------------------------------
241 
242         GeneratorUtils.element( w, "requiresDirectInvocation",
243                                 String.valueOf( mojoDescriptor.isDirectInvocationOnly() ) );
244 
245         // ----------------------------------------------------------------------
246         //
247         // ----------------------------------------------------------------------
248 
249         GeneratorUtils.element( w, "requiresProject", String.valueOf( mojoDescriptor.isProjectRequired() ) );
250 
251         // ----------------------------------------------------------------------
252         //
253         // ----------------------------------------------------------------------
254 
255         GeneratorUtils.element( w, "requiresReports", String.valueOf( mojoDescriptor.isRequiresReports() ) );
256 
257         // ----------------------------------------------------------------------
258         //
259         // ----------------------------------------------------------------------
260 
261         GeneratorUtils.element( w, "aggregator", String.valueOf( mojoDescriptor.isAggregator() ) );
262 
263         // ----------------------------------------------------------------------
264         //
265         // ----------------------------------------------------------------------
266 
267         GeneratorUtils.element( w, "requiresOnline", String.valueOf( mojoDescriptor.isOnlineRequired() ) );
268 
269         // ----------------------------------------------------------------------
270         //
271         // ----------------------------------------------------------------------
272 
273         GeneratorUtils.element( w, "inheritedByDefault", String.valueOf( mojoDescriptor.isInheritedByDefault() ) );
274 
275         // ----------------------------------------------------------------------
276         //
277         // ----------------------------------------------------------------------
278 
279         if ( StringUtils.isNotEmpty( mojoDescriptor.getPhase() ) )
280         {
281             GeneratorUtils.element( w, "phase", mojoDescriptor.getPhase() );
282         }
283 
284         // ----------------------------------------------------------------------
285         //
286         // ----------------------------------------------------------------------
287 
288         if ( StringUtils.isNotEmpty( mojoDescriptor.getExecutePhase() ) )
289         {
290             GeneratorUtils.element( w, "executePhase", mojoDescriptor.getExecutePhase() );
291         }
292 
293         if ( StringUtils.isNotEmpty( mojoDescriptor.getExecuteGoal() ) )
294         {
295             GeneratorUtils.element( w, "executeGoal", mojoDescriptor.getExecuteGoal() );
296         }
297 
298         if ( StringUtils.isNotEmpty( mojoDescriptor.getExecuteLifecycle() ) )
299         {
300             GeneratorUtils.element( w, "executeLifecycle", mojoDescriptor.getExecuteLifecycle() );
301         }
302 
303         // ----------------------------------------------------------------------
304         //
305         // ----------------------------------------------------------------------
306 
307         w.startElement( "implementation" );
308         w.writeText( mojoDescriptor.getImplementation() );
309         w.endElement();
310 
311         // ----------------------------------------------------------------------
312         //
313         // ----------------------------------------------------------------------
314 
315         w.startElement( "language" );
316         w.writeText( mojoDescriptor.getLanguage() );
317         w.endElement();
318 
319         // ----------------------------------------------------------------------
320         //
321         // ----------------------------------------------------------------------
322 
323         if ( StringUtils.isNotEmpty( mojoDescriptor.getComponentConfigurator() ) )
324         {
325             w.startElement( "configurator" );
326             w.writeText( mojoDescriptor.getComponentConfigurator() );
327             w.endElement();
328         }
329 
330         // ----------------------------------------------------------------------
331         //
332         // ----------------------------------------------------------------------
333 
334         if ( StringUtils.isNotEmpty( mojoDescriptor.getComponentComposer() ) )
335         {
336             w.startElement( "composer" );
337             w.writeText( mojoDescriptor.getComponentComposer() );
338             w.endElement();
339         }
340 
341         // ----------------------------------------------------------------------
342         //
343         // ----------------------------------------------------------------------
344 
345         w.startElement( "instantiationStrategy" );
346         w.writeText( mojoDescriptor.getInstantiationStrategy() );
347         w.endElement();
348 
349         // ----------------------------------------------------------------------
350         // Strategy for handling repeated reference to mojo in
351         // the calculated (decorated, resolved) execution stack
352         // ----------------------------------------------------------------------
353         w.startElement( "executionStrategy" );
354         w.writeText( mojoDescriptor.getExecutionStrategy() );
355         w.endElement();
356 
357         // ----------------------------------------------------------------------
358         //
359         // ----------------------------------------------------------------------
360 
361         if ( mojoDescriptor.getSince() != null )
362         {
363             w.startElement( "since" );
364 
365             if ( StringUtils.isEmpty( mojoDescriptor.getSince() ) )
366             {
367                 w.writeText( "No version given" );
368             }
369             else
370             {
371                 w.writeText( mojoDescriptor.getSince() );
372             }
373 
374             w.endElement();
375         }
376 
377         // ----------------------------------------------------------------------
378         //
379         // ----------------------------------------------------------------------
380 
381         if ( mojoDescriptor.getDeprecated() != null )
382         {
383             w.startElement( "deprecated" );
384 
385             if ( StringUtils.isEmpty( mojoDescriptor.getDeprecated() ) )
386             {
387                 w.writeText( "No reason given" );
388             }
389             else
390             {
391                 w.writeText( mojoDescriptor.getDeprecated() );
392             }
393 
394             w.endElement();
395         }
396 
397         // ----------------------------------------------------------------------
398         // Extended (3.0) descriptor
399         // ----------------------------------------------------------------------
400 
401         if ( mojoDescriptor instanceof ExtendedMojoDescriptor )
402         {
403             ExtendedMojoDescriptor extendedMojoDescriptor = (ExtendedMojoDescriptor) mojoDescriptor;
404             if ( extendedMojoDescriptor.getDependencyCollectionRequired() != null )
405             {
406                 GeneratorUtils.element( w, "requiresDependencyCollection",
407                                         extendedMojoDescriptor.getDependencyCollectionRequired() );
408             }
409 
410             GeneratorUtils.element( w, "threadSafe", String.valueOf( extendedMojoDescriptor.isThreadSafe() ) );
411         }
412 
413         // ----------------------------------------------------------------------
414         // Parameters
415         // ----------------------------------------------------------------------
416 
417         @SuppressWarnings( "unchecked" ) List<Parameter> parameters = mojoDescriptor.getParameters();
418 
419         w.startElement( "parameters" );
420 
421         Map<String, Requirement> requirements = new LinkedHashMap<String, Requirement>();
422 
423         Set<Parameter> configuration = new LinkedHashSet<Parameter>();
424 
425         if ( parameters != null )
426         {
427             if ( helpDescriptor )
428             {
429                 PluginUtils.sortMojoParameters( parameters );
430             }
431 
432             for ( Parameter parameter : parameters )
433             {
434                 String expression = getExpression( parameter );
435 
436                 if ( StringUtils.isNotEmpty( expression ) && expression.startsWith( "${component." ) )
437                 {
438                     // treat it as a component...a requirement, in other words.
439 
440                     // remove "component." plus expression delimiters
441                     String role = expression.substring( "${component.".length(), expression.length() - 1 );
442 
443                     String roleHint = null;
444 
445                     int posRoleHintSeparator = role.indexOf( '#' );
446                     if ( posRoleHintSeparator > 0 )
447                     {
448                         roleHint = role.substring( posRoleHintSeparator + 1 );
449 
450                         role = role.substring( 0, posRoleHintSeparator );
451                     }
452 
453                     // TODO: remove deprecated expression
454                     requirements.put( parameter.getName(), new Requirement( role, roleHint ) );
455                 }
456                 else if ( parameter.getRequirement() != null )
457                 {
458                     requirements.put( parameter.getName(), parameter.getRequirement() );
459                 }
460                 else if ( !helpDescriptor || parameter.isEditable() ) // don't show readonly parameters in help
461                 {
462                     // treat it as a normal parameter.
463 
464                     w.startElement( "parameter" );
465 
466                     GeneratorUtils.element( w, "name", parameter.getName() );
467 
468                     if ( parameter.getAlias() != null )
469                     {
470                         GeneratorUtils.element( w, "alias", parameter.getAlias() );
471                     }
472 
473                     GeneratorUtils.element( w, "type", parameter.getType() );
474 
475                     if ( parameter.getSince() != null )
476                     {
477                         w.startElement( "since" );
478 
479                         if ( StringUtils.isEmpty( parameter.getSince() ) )
480                         {
481                             w.writeText( "No version given" );
482                         }
483                         else
484                         {
485                             w.writeText( parameter.getSince() );
486                         }
487 
488                         w.endElement();
489                     }
490 
491                     if ( parameter.getDeprecated() != null )
492                     {
493                         if ( StringUtils.isEmpty( parameter.getDeprecated() ) )
494                         {
495                             GeneratorUtils.element( w, "deprecated", "No reason given" );
496                         }
497                         else
498                         {
499                             GeneratorUtils.element( w, "deprecated", parameter.getDeprecated() );
500                         }
501                     }
502 
503                     if ( parameter.getImplementation() != null )
504                     {
505                         GeneratorUtils.element( w, "implementation", parameter.getImplementation() );
506                     }
507 
508                     GeneratorUtils.element( w, "required", Boolean.toString( parameter.isRequired() ) );
509 
510                     GeneratorUtils.element( w, "editable", Boolean.toString( parameter.isEditable() ) );
511 
512                     GeneratorUtils.element( w, "description", parameter.getDescription(), helpDescriptor );
513 
514                     if ( StringUtils.isNotEmpty( parameter.getDefaultValue() ) || StringUtils.isNotEmpty(
515                         parameter.getExpression() ) )
516                     {
517                         configuration.add( parameter );
518                     }
519 
520                     w.endElement();
521                 }
522 
523             }
524         }
525 
526         w.endElement();
527 
528         // ----------------------------------------------------------------------
529         // Configuration
530         // ----------------------------------------------------------------------
531 
532         if ( !configuration.isEmpty() )
533         {
534             w.startElement( "configuration" );
535 
536             for ( Parameter parameter : configuration )
537             {
538                 if ( helpDescriptor && !parameter.isEditable() )
539                 {
540                     // don't show readonly parameters in help
541                     continue;
542                 }
543 
544                 w.startElement( parameter.getName() );
545 
546                 String type = parameter.getType();
547                 if ( StringUtils.isNotEmpty( type ) )
548                 {
549                     w.addAttribute( "implementation", type );
550                 }
551 
552                 if ( parameter.getDefaultValue() != null )
553                 {
554                     w.addAttribute( "default-value", parameter.getDefaultValue() );
555                 }
556 
557                 if ( StringUtils.isNotEmpty( parameter.getExpression() ) )
558                 {
559                     w.writeText( parameter.getExpression() );
560                 }
561 
562                 w.endElement();
563             }
564 
565             w.endElement();
566         }
567 
568         // ----------------------------------------------------------------------
569         // Requirements
570         // ----------------------------------------------------------------------
571 
572         if ( !requirements.isEmpty() && !helpDescriptor )
573         {
574             w.startElement( "requirements" );
575 
576             for ( Map.Entry<String, Requirement> entry : requirements.entrySet() )
577             {
578                 String key = entry.getKey();
579                 Requirement requirement = entry.getValue();
580 
581                 w.startElement( "requirement" );
582 
583                 GeneratorUtils.element( w, "role", requirement.getRole() );
584 
585                 if ( StringUtils.isNotEmpty( requirement.getRoleHint() ) )
586                 {
587                     GeneratorUtils.element( w, "role-hint", requirement.getRoleHint() );
588                 }
589 
590                 GeneratorUtils.element( w, "field-name", key );
591 
592                 w.endElement();
593             }
594 
595             w.endElement();
596         }
597 
598         w.endElement();
599     }
600 
601     /**
602      * Get the expression value, eventually surrounding it with <code>${ }</code>.
603      *
604      * @param parameter the parameter
605      * @return the expression value
606      */
607     private String getExpression( Parameter parameter )
608     {
609         String expression = parameter.getExpression();
610         if ( StringUtils.isNotBlank( expression ) && !expression.contains( "${" ) )
611         {
612             expression = "${" + expression.trim() + "}";
613             parameter.setExpression( expression );
614         }
615         return expression;
616     }
617 }