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