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