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