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