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 }