001 package org.apache.maven.tools.plugin.generator;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import java.io.File;
023 import java.io.FileOutputStream;
024 import java.io.IOException;
025 import java.io.OutputStreamWriter;
026 import java.io.Writer;
027 import java.text.SimpleDateFormat;
028 import java.util.Date;
029 import java.util.LinkedHashMap;
030 import java.util.LinkedHashSet;
031 import java.util.List;
032 import java.util.Map;
033 import java.util.Set;
034 import org.apache.maven.plugin.descriptor.DuplicateMojoDescriptorException;
035 import org.apache.maven.plugin.descriptor.MojoDescriptor;
036 import org.apache.maven.plugin.descriptor.Parameter;
037 import org.apache.maven.plugin.descriptor.PluginDescriptor;
038 import org.apache.maven.plugin.descriptor.Requirement;
039 import org.apache.maven.plugin.logging.Log;
040 import org.apache.maven.project.MavenProject;
041 import org.apache.maven.tools.plugin.ExtendedMojoDescriptor;
042 import org.apache.maven.tools.plugin.PluginToolsRequest;
043 import org.apache.maven.tools.plugin.util.PluginUtils;
044 import org.codehaus.plexus.logging.Logger;
045 import org.codehaus.plexus.util.IOUtil;
046 import org.codehaus.plexus.util.StringUtils;
047 import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
048 import org.codehaus.plexus.util.xml.XMLWriter;
049
050 /**
051 * Generate a <a href="/ref/current/maven-plugin-api/plugin.html">Maven Plugin Descriptor XML file</a> and
052 * corresponding <code>plugin-help.xml</code> help content for {@link PluginHelpGenerator}.
053 *
054 * @version $Id: PluginDescriptorGenerator.java 1406615 2012-11-07 13:26:25Z krosenvold $
055 * @todo add example usage tag that can be shown in the doco
056 * @todo need to add validation directives so that systems embedding maven2 can
057 * get validation directives to help users in IDEs.
058 */
059 public class PluginDescriptorGenerator
060 implements Generator
061 {
062
063 private final Log log;
064
065 public PluginDescriptorGenerator( Log log )
066 {
067 this.log = log;
068 }
069
070 /**
071 * {@inheritDoc}
072 */
073 public void execute( File destinationDirectory, PluginToolsRequest request )
074 throws GeneratorException
075 {
076 // eventually rewrite help mojo class to match actual package name
077 PluginHelpGenerator.rewriteHelpMojo( request, log );
078
079 try
080 {
081 // write complete plugin.xml descriptor
082 File f = new File( destinationDirectory, "plugin.xml" );
083 writeDescriptor( f, request, false );
084
085 // write plugin-help.xml help-descriptor
086 MavenProject mavenProject = request.getProject();
087
088 f = new File( mavenProject.getBuild().getOutputDirectory(),
089 PluginHelpGenerator.getPluginHelpPath( mavenProject ) );
090
091 writeDescriptor( f, request, true );
092 }
093 catch ( IOException e )
094 {
095 throw new GeneratorException( e.getMessage(), e );
096 }
097 catch ( DuplicateMojoDescriptorException e )
098 {
099 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 }