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