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 org.apache.maven.plugin.descriptor.DuplicateMojoDescriptorException; 023 import org.apache.maven.plugin.descriptor.MojoDescriptor; 024 import org.apache.maven.plugin.descriptor.Parameter; 025 import org.apache.maven.plugin.descriptor.PluginDescriptor; 026 import org.apache.maven.plugin.descriptor.Requirement; 027 import org.apache.maven.project.MavenProject; 028 import org.apache.maven.tools.plugin.ExtendedMojoDescriptor; 029 import org.apache.maven.tools.plugin.PluginToolsRequest; 030 import org.apache.maven.tools.plugin.util.PluginUtils; 031 import org.codehaus.plexus.util.IOUtil; 032 import org.codehaus.plexus.util.StringUtils; 033 import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter; 034 import org.codehaus.plexus.util.xml.XMLWriter; 035 036 import java.io.File; 037 import java.io.FileOutputStream; 038 import java.io.IOException; 039 import java.io.OutputStreamWriter; 040 import java.io.Writer; 041 import java.text.SimpleDateFormat; 042 import java.util.Date; 043 import java.util.LinkedHashMap; 044 import java.util.LinkedHashSet; 045 import java.util.List; 046 import java.util.Map; 047 import java.util.Set; 048 049 /** 050 * Generate a <a href="/ref/current/maven-plugin-api/plugin.html">Maven Plugin Descriptor XML file</a> and 051 * corresponding help content. 052 * 053 * @version $Id: PluginDescriptorGenerator.java 1354239 2012-06-26 21:19:06Z hboutemy $ 054 * @todo add example usage tag that can be shown in the doco 055 * @todo need to add validation directives so that systems embedding maven2 can 056 * get validation directives to help users in IDEs. 057 */ 058 public class PluginDescriptorGenerator 059 implements Generator 060 { 061 062 /** 063 * {@inheritDoc} 064 */ 065 public void execute( File destinationDirectory, PluginToolsRequest request ) 066 throws GeneratorException 067 { 068 // eventually rewrite help mojo class to match actual package name 069 PluginHelpGenerator.rewriteHelpMojo( request ); 070 071 try 072 { 073 // write complete plugin.xml descriptor 074 File f = new File( destinationDirectory, "plugin.xml" ); 075 writeDescriptor( f, request, false ); 076 077 // write plugin-help.xml help-descriptor 078 MavenProject mavenProject = request.getProject(); 079 String pluginHelpFilePath = 080 "META-INF/maven/" + mavenProject.getGroupId() + "/" + mavenProject.getArtifactId() + "/plugin-help.xml"; 081 082 f = new File( request.getProject().getBuild().getOutputDirectory(), pluginHelpFilePath ); 083 writeDescriptor( f, request, true ); 084 } 085 catch ( IOException e ) 086 { 087 throw new GeneratorException( e.getMessage(), e ); 088 } 089 catch ( DuplicateMojoDescriptorException e ) 090 { 091 throw new GeneratorException( e.getMessage(), e ); 092 } 093 } 094 095 private String getVersion() 096 { 097 Package p = this.getClass().getPackage(); 098 String version = ( p == null ) ? null : p.getSpecificationVersion(); 099 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 }