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.text.SimpleDateFormat;
28  import java.util.Date;
29  import java.util.LinkedHashMap;
30  import java.util.LinkedHashSet;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Set;
34  import org.apache.maven.plugin.descriptor.DuplicateMojoDescriptorException;
35  import org.apache.maven.plugin.descriptor.MojoDescriptor;
36  import org.apache.maven.plugin.descriptor.Parameter;
37  import org.apache.maven.plugin.descriptor.PluginDescriptor;
38  import org.apache.maven.plugin.descriptor.Requirement;
39  import org.apache.maven.plugin.logging.Log;
40  import org.apache.maven.project.MavenProject;
41  import org.apache.maven.tools.plugin.ExtendedMojoDescriptor;
42  import org.apache.maven.tools.plugin.PluginToolsRequest;
43  import org.apache.maven.tools.plugin.util.PluginUtils;
44  import org.codehaus.plexus.util.IOUtil;
45  import org.codehaus.plexus.util.StringUtils;
46  import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
47  import org.codehaus.plexus.util.xml.XMLWriter;
48  
49  /**
50   * Generate a <a href="/ref/current/maven-plugin-api/plugin.html">Maven Plugin Descriptor XML file</a> and
51   * corresponding <code>plugin-help.xml</code> help content for {@link PluginHelpGenerator}.
52   *
53   * @version $Id: PluginDescriptorGenerator.html 995996 2016-08-26 22:31:42Z rfscholte $
54   */
55  public class PluginDescriptorGenerator
56      implements Generator
57  {
58  
59      private final Log log;
60  
61      public PluginDescriptorGenerator( Log log )
62      {
63          this.log = log;
64      }
65  
66      /**
67       * {@inheritDoc}
68       */
69      public void execute( File destinationDirectory, PluginToolsRequest request )
70          throws GeneratorException
71      {
72          // eventually rewrite help mojo class to match actual package name
73          PluginHelpGenerator.rewriteHelpMojo( request, log );
74  
75          try
76          {
77              // write complete plugin.xml descriptor
78              File f = new File( destinationDirectory, "plugin.xml" );
79              writeDescriptor( f, request, false );
80  
81              // write plugin-help.xml help-descriptor
82              MavenProject mavenProject = request.getProject();
83  
84              f = new File( mavenProject.getBuild().getOutputDirectory(),
85                            PluginHelpGenerator.getPluginHelpPath( mavenProject ) );
86  
87              writeDescriptor( f, request, true );
88          }
89          catch ( IOException e )
90          {
91              throw new GeneratorException( e.getMessage(), e );
92          }
93          catch ( DuplicateMojoDescriptorException e )
94          {
95              throw new GeneratorException( e.getMessage(), e );
96          }
97      }
98  
99      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 }