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