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