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.IOException; 024import java.io.OutputStreamWriter; 025import java.io.PrintWriter; 026import java.io.Writer; 027import java.net.URI; 028import java.net.URISyntaxException; 029import java.text.MessageFormat; 030import java.util.ArrayList; 031import java.util.Iterator; 032import java.util.List; 033import java.util.Locale; 034import java.util.ResourceBundle; 035import java.util.regex.Matcher; 036import java.util.regex.Pattern; 037 038import org.apache.maven.plugin.descriptor.MojoDescriptor; 039import org.apache.maven.plugin.descriptor.Parameter; 040import org.apache.maven.project.MavenProject; 041import org.apache.maven.tools.plugin.EnhancedParameterWrapper; 042import org.apache.maven.tools.plugin.ExtendedMojoDescriptor; 043import org.apache.maven.tools.plugin.PluginToolsRequest; 044import org.apache.maven.tools.plugin.javadoc.JavadocLinkGenerator; 045import org.codehaus.plexus.util.StringUtils; 046import org.codehaus.plexus.util.io.CachingOutputStream; 047import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter; 048import org.codehaus.plexus.util.xml.XMLWriter; 049import org.slf4j.Logger; 050import org.slf4j.LoggerFactory; 051 052import static java.nio.charset.StandardCharsets.UTF_8; 053 054/** 055 * Generate <a href="https://maven.apache.org/doxia/references/xdoc-format.html">xdoc documentation</a> for each mojo. 056 */ 057public class PluginXdocGenerator 058 implements Generator 059{ 060 /** 061 * Regular expression matching an XHTML link 062 * group 1 = link target, group 2 = link label 063 */ 064 private static final Pattern HTML_LINK_PATTERN = Pattern.compile( "<a href=\\\"([^\\\"]*)\\\">(.*?)</a>" ); 065 066 private static final Logger LOG = LoggerFactory.getLogger( PluginXdocGenerator.class ); 067 068 /** 069 * locale 070 */ 071 private final Locale locale; 072 073 /** 074 * project 075 */ 076 private final MavenProject project; 077 078 /** 079 * The directory where the generated site is written. 080 * Used for resolving relative links to javadoc. 081 */ 082 private final File reportOutputDirectory; 083 084 private final boolean disableInternalJavadocLinkValidation; 085 086 /** 087 * Default constructor using <code>Locale.ENGLISH</code> as locale. 088 * Used only in test cases. 089 */ 090 public PluginXdocGenerator() 091 { 092 this( null ); 093 } 094 095 /** 096 * Constructor using <code>Locale.ENGLISH</code> as locale. 097 * 098 * @param project not null Maven project. 099 */ 100 public PluginXdocGenerator( MavenProject project ) 101 { 102 this( project, Locale.ENGLISH, new File( "" ).getAbsoluteFile(), false ); 103 } 104 105 /** 106 * @param project not null. 107 * @param locale not null wanted locale. 108 */ 109 public PluginXdocGenerator( MavenProject project, Locale locale, File reportOutputDirectory, 110 boolean disableInternalJavadocLinkValidation ) 111 { 112 this.project = project; 113 if ( locale == null ) 114 { 115 this.locale = Locale.ENGLISH; 116 } 117 else 118 { 119 this.locale = locale; 120 } 121 this.reportOutputDirectory = reportOutputDirectory; 122 this.disableInternalJavadocLinkValidation = disableInternalJavadocLinkValidation; 123 } 124 125 126 /** 127 * {@inheritDoc} 128 */ 129 @Override 130 public void execute( File destinationDirectory, PluginToolsRequest request ) 131 throws GeneratorException 132 { 133 try 134 { 135 if ( request.getPluginDescriptor().getMojos() != null ) 136 { 137 List<MojoDescriptor> mojos = request.getPluginDescriptor().getMojos(); 138 for ( MojoDescriptor descriptor : mojos ) 139 { 140 processMojoDescriptor( descriptor, destinationDirectory ); 141 } 142 } 143 } 144 catch ( IOException e ) 145 { 146 throw new GeneratorException( e.getMessage(), e ); 147 } 148 149 } 150 151 /** 152 * @param mojoDescriptor not null 153 * @param destinationDirectory not null 154 * @throws IOException if any 155 */ 156 protected void processMojoDescriptor( MojoDescriptor mojoDescriptor, File destinationDirectory ) 157 throws IOException 158 { 159 File outputFile = new File( destinationDirectory, getMojoFilename( mojoDescriptor, "xml" ) ); 160 try ( Writer writer = new OutputStreamWriter( new CachingOutputStream( outputFile ), UTF_8 ) ) 161 { 162 XMLWriter w = new PrettyPrintXMLWriter( new PrintWriter( writer ), UTF_8.name(), null ); 163 writeBody( mojoDescriptor, w ); 164 165 writer.flush(); 166 } 167 } 168 169 /** 170 * @param mojo not null 171 * @param ext not null 172 * @return the output file name 173 */ 174 private String getMojoFilename( MojoDescriptor mojo, String ext ) 175 { 176 return mojo.getGoal() + "-mojo." + ext; 177 } 178 179 /** 180 * @param mojoDescriptor not null 181 * @param w not null 182 */ 183 private void writeBody( MojoDescriptor mojoDescriptor, XMLWriter w ) 184 { 185 w.startElement( "document" ); 186 w.addAttribute( "xmlns", "http://maven.apache.org/XDOC/2.0" ); 187 w.addAttribute( "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" ); 188 w.addAttribute( "xsi:schemaLocation", 189 "http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd" ); 190 191 // ---------------------------------------------------------------------- 192 // 193 // ---------------------------------------------------------------------- 194 195 w.startElement( "properties" ); 196 197 w.startElement( "title" ); 198 w.writeText( mojoDescriptor.getFullGoalName() ); 199 w.endElement(); // title 200 201 w.endElement(); // properties 202 203 // ---------------------------------------------------------------------- 204 // 205 // ---------------------------------------------------------------------- 206 207 w.startElement( "body" ); 208 209 w.startElement( "section" ); 210 211 w.addAttribute( "name", mojoDescriptor.getFullGoalName() ); 212 213 writeReportNotice( mojoDescriptor, w ); 214 215 w.startElement( "p" ); 216 w.writeMarkup( getString( "pluginxdoc.mojodescriptor.fullname" ) ); 217 w.endElement(); //p 218 w.startElement( "p" ); 219 w.writeMarkup( mojoDescriptor.getPluginDescriptor().getGroupId() + ":" 220 + mojoDescriptor.getPluginDescriptor().getArtifactId() + ":" 221 + mojoDescriptor.getPluginDescriptor().getVersion() + ":" + mojoDescriptor.getGoal() ); 222 w.endElement(); //p 223 224 String context = "goal " + mojoDescriptor.getGoal(); 225 if ( StringUtils.isNotEmpty( mojoDescriptor.getDeprecated() ) ) 226 { 227 w.startElement( "p" ); 228 w.writeMarkup( getString( "pluginxdoc.mojodescriptor.deprecated" ) ); 229 w.endElement(); // p 230 w.startElement( "div" ); 231 w.writeMarkup( getXhtmlWithValidatedLinks( mojoDescriptor.getDeprecated(), context ) ); 232 w.endElement(); // div 233 } 234 235 w.startElement( "p" ); 236 w.writeMarkup( getString( "pluginxdoc.description" ) ); 237 w.endElement(); //p 238 w.startElement( "div" ); 239 if ( StringUtils.isNotEmpty( mojoDescriptor.getDescription() ) ) 240 { 241 w.writeMarkup( getXhtmlWithValidatedLinks( mojoDescriptor.getDescription(), context ) ); 242 } 243 else 244 { 245 w.writeText( getString( "pluginxdoc.nodescription" ) ); 246 } 247 w.endElement(); // div 248 249 writeGoalAttributes( mojoDescriptor, w ); 250 251 writeGoalParameterTable( mojoDescriptor, w ); 252 253 w.endElement(); // section 254 255 w.endElement(); // body 256 257 w.endElement(); // document 258 } 259 260 /** 261 * @param mojoDescriptor not null 262 * @param w not null 263 */ 264 private void writeReportNotice( MojoDescriptor mojoDescriptor, XMLWriter w ) 265 { 266 if ( GeneratorUtils.isMavenReport( mojoDescriptor.getImplementation(), project ) ) 267 { 268 w.startElement( "p" ); 269 w.writeMarkup( getString( "pluginxdoc.mojodescriptor.notice.note" ) ); 270 w.writeText( getString( "pluginxdoc.mojodescriptor.notice.isMavenReport" ) ); 271 w.endElement(); //p 272 } 273 } 274 275 /** 276 * @param mojoDescriptor not null 277 * @param w not null 278 */ 279 private void writeGoalAttributes( MojoDescriptor mojoDescriptor, XMLWriter w ) 280 { 281 w.startElement( "p" ); 282 w.writeMarkup( getString( "pluginxdoc.mojodescriptor.attributes" ) ); 283 w.endElement(); //p 284 285 boolean addedUl = false; 286 String value; 287 if ( mojoDescriptor.isProjectRequired() ) 288 { 289 addedUl = addUl( w, addedUl ); 290 w.startElement( "li" ); 291 w.writeMarkup( getString( "pluginxdoc.mojodescriptor.projectRequired" ) ); 292 w.endElement(); //li 293 } 294 295 if ( mojoDescriptor.isRequiresReports() ) 296 { 297 addedUl = addUl( w, addedUl ); 298 w.startElement( "li" ); 299 w.writeMarkup( getString( "pluginxdoc.mojodescriptor.reportingMojo" ) ); 300 w.endElement(); // li 301 } 302 303 if ( mojoDescriptor.isAggregator() ) 304 { 305 addedUl = addUl( w, addedUl ); 306 w.startElement( "li" ); 307 w.writeMarkup( getString( "pluginxdoc.mojodescriptor.aggregator" ) ); 308 w.endElement(); //li 309 } 310 311 if ( mojoDescriptor.isDirectInvocationOnly() ) 312 { 313 addedUl = addUl( w, addedUl ); 314 w.startElement( "li" ); 315 w.writeMarkup( getString( "pluginxdoc.mojodescriptor.directInvocationOnly" ) ); 316 w.endElement(); //li 317 } 318 319 value = mojoDescriptor.isDependencyResolutionRequired(); 320 if ( StringUtils.isNotEmpty( value ) ) 321 { 322 addedUl = addUl( w, addedUl ); 323 w.startElement( "li" ); 324 w.writeMarkup( format( "pluginxdoc.mojodescriptor.dependencyResolutionRequired", value ) ); 325 w.endElement(); //li 326 } 327 328 if ( mojoDescriptor instanceof ExtendedMojoDescriptor ) 329 { 330 ExtendedMojoDescriptor extendedMojoDescriptor = (ExtendedMojoDescriptor) mojoDescriptor; 331 332 value = extendedMojoDescriptor.getDependencyCollectionRequired(); 333 if ( StringUtils.isNotEmpty( value ) ) 334 { 335 addedUl = addUl( w, addedUl ); 336 w.startElement( "li" ); 337 w.writeMarkup( format( "pluginxdoc.mojodescriptor.dependencyCollectionRequired", value ) ); 338 w.endElement(); //li 339 } 340 } 341 342 addedUl = addUl( w, addedUl ); 343 w.startElement( "li" ); 344 w.writeMarkup( getString( mojoDescriptor.isThreadSafe() 345 ? "pluginxdoc.mojodescriptor.threadSafe" 346 : "pluginxdoc.mojodescriptor.notThreadSafe" ) ); 347 w.endElement(); //li 348 349 value = mojoDescriptor.getSince(); 350 if ( StringUtils.isNotEmpty( value ) ) 351 { 352 addedUl = addUl( w, addedUl ); 353 w.startElement( "li" ); 354 w.writeMarkup( format( "pluginxdoc.mojodescriptor.since", value ) ); 355 w.endElement(); //li 356 } 357 358 value = mojoDescriptor.getPhase(); 359 if ( StringUtils.isNotEmpty( value ) ) 360 { 361 addedUl = addUl( w, addedUl ); 362 w.startElement( "li" ); 363 w.writeMarkup( format( "pluginxdoc.mojodescriptor.phase", value ) ); 364 w.endElement(); //li 365 } 366 367 value = mojoDescriptor.getExecutePhase(); 368 if ( StringUtils.isNotEmpty( value ) ) 369 { 370 addedUl = addUl( w, addedUl ); 371 w.startElement( "li" ); 372 w.writeMarkup( format( "pluginxdoc.mojodescriptor.executePhase", value ) ); 373 w.endElement(); //li 374 } 375 376 value = mojoDescriptor.getExecuteGoal(); 377 if ( StringUtils.isNotEmpty( value ) ) 378 { 379 addedUl = addUl( w, addedUl ); 380 w.startElement( "li" ); 381 w.writeMarkup( format( "pluginxdoc.mojodescriptor.executeGoal", value ) ); 382 w.endElement(); //li 383 } 384 385 value = mojoDescriptor.getExecuteLifecycle(); 386 if ( StringUtils.isNotEmpty( value ) ) 387 { 388 addedUl = addUl( w, addedUl ); 389 w.startElement( "li" ); 390 w.writeMarkup( format( "pluginxdoc.mojodescriptor.executeLifecycle", value ) ); 391 w.endElement(); //li 392 } 393 394 if ( mojoDescriptor.isOnlineRequired() ) 395 { 396 addedUl = addUl( w, addedUl ); 397 w.startElement( "li" ); 398 w.writeMarkup( getString( "pluginxdoc.mojodescriptor.onlineRequired" ) ); 399 w.endElement(); //li 400 } 401 402 if ( !mojoDescriptor.isInheritedByDefault() ) 403 { 404 addedUl = addUl( w, addedUl ); 405 w.startElement( "li" ); 406 w.writeMarkup( getString( "pluginxdoc.mojodescriptor.inheritedByDefault" ) ); 407 w.endElement(); //li 408 } 409 410 if ( addedUl ) 411 { 412 w.endElement(); //ul 413 } 414 } 415 416 /** 417 * @param mojoDescriptor not null 418 * @param w not null 419 */ 420 private void writeGoalParameterTable( MojoDescriptor mojoDescriptor, XMLWriter w ) 421 { 422 List<Parameter> parameterList = mojoDescriptor.getParameters(); 423 424 // remove components and read-only parameters 425 List<Parameter> list = filterParameters( parameterList ); 426 427 if ( !list.isEmpty() ) 428 { 429 writeParameterSummary( list, w, mojoDescriptor.getGoal() ); 430 writeParameterDetails( list, w, mojoDescriptor.getGoal() ); 431 } 432 else 433 { 434 w.startElement( "subsection" ); 435 w.addAttribute( "name", getString( "pluginxdoc.mojodescriptor.parameters" ) ); 436 437 w.startElement( "p" ); 438 w.writeMarkup( getString( "pluginxdoc.mojodescriptor.noParameter" ) ); 439 w.endElement(); //p 440 441 w.endElement(); 442 } 443 } 444 445 /** 446 * Filter parameters to only retain those which must be documented, i.e. neither components nor readonly. 447 * 448 * @param parameterList not null 449 * @return the parameters list without components. 450 */ 451 private List<Parameter> filterParameters( List<Parameter> parameterList ) 452 { 453 List<Parameter> filtered = new ArrayList<>(); 454 455 if ( parameterList != null ) 456 { 457 for ( Parameter parameter : parameterList ) 458 { 459 if ( parameter.isEditable() ) 460 { 461 String expression = parameter.getExpression(); 462 463 if ( expression == null || !expression.startsWith( "${component." ) ) 464 { 465 filtered.add( parameter ); 466 } 467 } 468 } 469 } 470 471 return filtered; 472 } 473 474 /** 475 * @param parameterList not null 476 * @param w not null 477 */ 478 private void writeParameterDetails( List<Parameter> parameterList, XMLWriter w, String goal ) 479 { 480 w.startElement( "subsection" ); 481 w.addAttribute( "name", getString( "pluginxdoc.mojodescriptor.parameter.details" ) ); 482 483 for ( Iterator<Parameter> parameters = parameterList.iterator(); parameters.hasNext(); ) 484 { 485 Parameter parameter = parameters.next(); 486 487 w.startElement( "h4" ); 488 w.writeMarkup( format( "pluginxdoc.mojodescriptor.parameter.name_internal", parameter.getName() ) ); 489 w.endElement(); 490 491 String context = "Parameter " + parameter.getName() + " in goal " + goal; 492 if ( StringUtils.isNotEmpty( parameter.getDeprecated() ) ) 493 { 494 w.startElement( "div" ); 495 String deprecated = getXhtmlWithValidatedLinks( parameter.getDeprecated(), context ); 496 w.writeMarkup( format( "pluginxdoc.mojodescriptor.parameter.deprecated", deprecated ) ); 497 w.endElement(); // div 498 } 499 500 w.startElement( "div" ); 501 if ( StringUtils.isNotEmpty( parameter.getDescription() ) ) 502 { 503 504 w.writeMarkup( getXhtmlWithValidatedLinks( parameter.getDescription(), context ) ); 505 } 506 else 507 { 508 w.writeMarkup( getString( "pluginxdoc.nodescription" ) ); 509 } 510 w.endElement(); // div 511 512 boolean addedUl = false; 513 addedUl = addUl( w, addedUl, parameter.getType() ); 514 String typeValue = getLinkedType( parameter, false ); 515 writeDetail( getString( "pluginxdoc.mojodescriptor.parameter.type" ), typeValue, w ); 516 517 if ( StringUtils.isNotEmpty( parameter.getSince() ) ) 518 { 519 addedUl = addUl( w, addedUl ); 520 writeDetail( getString( "pluginxdoc.mojodescriptor.parameter.since" ), parameter.getSince(), w ); 521 } 522 523 if ( parameter.isRequired() ) 524 { 525 addedUl = addUl( w, addedUl ); 526 writeDetail( getString( "pluginxdoc.mojodescriptor.parameter.required" ), getString( "pluginxdoc.yes" ), 527 w ); 528 } 529 else 530 { 531 addedUl = addUl( w, addedUl ); 532 writeDetail( getString( "pluginxdoc.mojodescriptor.parameter.required" ), getString( "pluginxdoc.no" ), 533 w ); 534 } 535 536 String expression = parameter.getExpression(); 537 addedUl = addUl( w, addedUl, expression ); 538 String property = getPropertyFromExpression( expression ); 539 if ( property == null ) 540 { 541 writeDetail( getString( "pluginxdoc.mojodescriptor.parameter.expression" ), expression, w ); 542 } 543 else 544 { 545 writeDetail( getString( "pluginxdoc.mojodescriptor.parameter.property" ), property, w ); 546 } 547 548 addedUl = addUl( w, addedUl, parameter.getDefaultValue() ); 549 writeDetail( getString( "pluginxdoc.mojodescriptor.parameter.default" ), 550 escapeXml( parameter.getDefaultValue() ), w ); 551 552 addedUl = addUl( w, addedUl, parameter.getAlias() ); 553 writeDetail( getString( "pluginxdoc.mojodescriptor.parameter.alias" ), escapeXml( parameter.getAlias() ), 554 w ); 555 556 if ( addedUl ) 557 { 558 w.endElement(); //ul 559 } 560 561 if ( parameters.hasNext() ) 562 { 563 w.writeMarkup( "<hr/>" ); 564 } 565 } 566 567 w.endElement(); 568 } 569 570 static String getShortType( String type ) 571 { 572 // split into type arguments and main type 573 int startTypeArguments = type.indexOf( '<' ); 574 if ( startTypeArguments == -1 ) 575 { 576 return getShortTypeOfSimpleType( type ); 577 } 578 else 579 { 580 StringBuilder shortType = new StringBuilder(); 581 shortType.append( getShortTypeOfSimpleType( type.substring( 0, startTypeArguments ) ) ); 582 shortType.append( "<" ) 583 .append( getShortTypeOfTypeArgument( 584 type.substring( startTypeArguments + 1, type.lastIndexOf( ">" ) ) ) ) 585 .append( ">" ); 586 return shortType.toString(); 587 } 588 589 } 590 591 private static String getShortTypeOfTypeArgument( String type ) 592 { 593 String[] typeArguments = type.split( ",\\s*" ); 594 StringBuilder shortType = new StringBuilder(); 595 for ( int i = 0; i < typeArguments.length; i++ ) 596 { 597 String typeArgument = typeArguments[i]; 598 if ( typeArgument.contains( "<" ) ) 599 { 600 // nested type arguments lead to ellipsis 601 return "..."; 602 } 603 else 604 { 605 shortType.append( getShortTypeOfSimpleType( typeArgument ) ); 606 if ( i < typeArguments.length - 1 ) 607 { 608 shortType.append( "," ); 609 } 610 } 611 } 612 return shortType.toString(); 613 } 614 615 private static String getShortTypeOfSimpleType( String type ) 616 { 617 int index = type.lastIndexOf( '.' ); 618 return type.substring( index + 1 ); 619 } 620 621 private String getLinkedType( Parameter parameter, boolean isShortType ) 622 { 623 final String typeValue; 624 if ( isShortType ) 625 { 626 typeValue = getShortType( parameter.getType() ); 627 } 628 else 629 { 630 typeValue = parameter.getType(); 631 } 632 if ( parameter instanceof EnhancedParameterWrapper ) 633 { 634 EnhancedParameterWrapper enhancedParameter = (EnhancedParameterWrapper) parameter; 635 if ( enhancedParameter.getTypeJavadocUrl() != null ) 636 { 637 URI javadocUrl = enhancedParameter.getTypeJavadocUrl(); 638 // optionally check if link is valid 639 if ( javadocUrl.isAbsolute() 640 || disableInternalJavadocLinkValidation 641 || JavadocLinkGenerator.isLinkValid( javadocUrl, reportOutputDirectory.toPath() ) ) 642 { 643 return format( "pluginxdoc.mojodescriptor.parameter.type_link", 644 new Object[] { escapeXml( typeValue ), enhancedParameter.getTypeJavadocUrl() } ); 645 } 646 } 647 } 648 return escapeXml( typeValue ); 649 } 650 651 private boolean addUl( XMLWriter w, boolean addedUl, String content ) 652 { 653 if ( StringUtils.isNotEmpty( content ) ) 654 { 655 return addUl( w, addedUl ); 656 } 657 return addedUl; 658 } 659 660 private boolean addUl( XMLWriter w, boolean addedUl ) 661 { 662 if ( !addedUl ) 663 { 664 w.startElement( "ul" ); 665 addedUl = true; 666 } 667 return addedUl; 668 } 669 670 private String getPropertyFromExpression( String expression ) 671 { 672 if ( StringUtils.isNotEmpty( expression ) && expression.startsWith( "${" ) && expression.endsWith( "}" ) 673 && !expression.substring( 2 ).contains( "${" ) ) 674 { 675 // expression="${xxx}" -> property="xxx" 676 return expression.substring( 2, expression.length() - 1 ); 677 } 678 // no property can be extracted 679 return null; 680 } 681 682 /** 683 * @param param not null 684 * @param value could be null 685 * @param w not null 686 */ 687 private void writeDetail( String param, String value, XMLWriter w ) 688 { 689 if ( StringUtils.isNotEmpty( value ) ) 690 { 691 w.startElement( "li" ); 692 w.writeMarkup( format( "pluginxdoc.detail", new String[]{ param, value } ) ); 693 w.endElement(); //li 694 } 695 } 696 697 /** 698 * @param parameterList not null 699 * @param w not null 700 */ 701 private void writeParameterSummary( List<Parameter> parameterList, XMLWriter w, String goal ) 702 { 703 List<Parameter> requiredParams = getParametersByRequired( true, parameterList ); 704 if ( !requiredParams.isEmpty() ) 705 { 706 writeParameterList( getString( "pluginxdoc.mojodescriptor.requiredParameters" ), 707 requiredParams, w, goal ); 708 } 709 710 List<Parameter> optionalParams = getParametersByRequired( false, parameterList ); 711 if ( !optionalParams.isEmpty() ) 712 { 713 writeParameterList( getString( "pluginxdoc.mojodescriptor.optionalParameters" ), 714 optionalParams, w, goal ); 715 } 716 } 717 718 /** 719 * @param title not null 720 * @param parameterList not null 721 * @param w not null 722 */ 723 private void writeParameterList( String title, List<Parameter> parameterList, XMLWriter w, String goal ) 724 { 725 w.startElement( "subsection" ); 726 w.addAttribute( "name", title ); 727 728 w.startElement( "table" ); 729 w.addAttribute( "border", "0" ); 730 731 w.startElement( "tr" ); 732 w.startElement( "th" ); 733 w.writeText( getString( "pluginxdoc.mojodescriptor.parameter.name" ) ); 734 w.endElement(); //th 735 w.startElement( "th" ); 736 w.writeText( getString( "pluginxdoc.mojodescriptor.parameter.type" ) ); 737 w.endElement(); //th 738 w.startElement( "th" ); 739 w.writeText( getString( "pluginxdoc.mojodescriptor.parameter.since" ) ); 740 w.endElement(); //th 741 w.startElement( "th" ); 742 w.writeText( getString( "pluginxdoc.mojodescriptor.parameter.description" ) ); 743 w.endElement(); //th 744 w.endElement(); //tr 745 746 for ( Parameter parameter : parameterList ) 747 { 748 w.startElement( "tr" ); 749 750 // name 751 w.startElement( "td" ); 752 w.writeMarkup( format( "pluginxdoc.mojodescriptor.parameter.name_link", parameter.getName() ) ); 753 w.endElement(); //td 754 755 //type 756 w.startElement( "td" ); 757 w.writeMarkup( "<code>" + getLinkedType( parameter, true ) + "</code>" ); 758 w.endElement(); //td 759 760 // since 761 w.startElement( "td" ); 762 if ( StringUtils.isNotEmpty( parameter.getSince() ) ) 763 { 764 w.writeMarkup( "<code>" + parameter.getSince() + "</code>" ); 765 } 766 else 767 { 768 w.writeMarkup( "<code>-</code>" ); 769 } 770 w.endElement(); //td 771 772 // description 773 w.startElement( "td" ); 774 String description; 775 String context = "Parameter " + parameter.getName() + " in goal " + goal; 776 if ( StringUtils.isNotEmpty( parameter.getDeprecated() ) ) 777 { 778 String deprecated = getXhtmlWithValidatedLinks( parameter.getDescription(), context ); 779 description = format( "pluginxdoc.mojodescriptor.parameter.deprecated", deprecated ); 780 } 781 else if ( StringUtils.isNotEmpty( parameter.getDescription() ) ) 782 { 783 description = getXhtmlWithValidatedLinks( parameter.getDescription(), context ); 784 } 785 else 786 { 787 description = getString( "pluginxdoc.nodescription" ); 788 } 789 w.writeMarkup( description + "<br/>" ); 790 791 if ( StringUtils.isNotEmpty( parameter.getDefaultValue() ) ) 792 { 793 w.writeMarkup( format( "pluginxdoc.mojodescriptor.parameter.defaultValue", 794 escapeXml( parameter.getDefaultValue() ) ) ); 795 w.writeMarkup( "<br/>" ); 796 } 797 798 String property = getPropertyFromExpression( parameter.getExpression() ); 799 if ( property != null ) 800 { 801 w.writeMarkup( format( "pluginxdoc.mojodescriptor.parameter.property.description", property ) ); 802 w.writeMarkup( "<br/>" ); 803 } 804 805 if ( StringUtils.isNotEmpty( parameter.getAlias() ) ) 806 { 807 w.writeMarkup( format( "pluginxdoc.mojodescriptor.parameter.alias.description", 808 escapeXml( parameter.getAlias() ) ) ); 809 } 810 811 w.endElement(); //td 812 w.endElement(); //tr 813 } 814 815 w.endElement(); //table 816 w.endElement(); //section 817 } 818 819 /** 820 * @param required <code>true</code> for required parameters, <code>false</code> otherwise. 821 * @param parameterList not null 822 * @return list of parameters depending the value of <code>required</code> 823 */ 824 private List<Parameter> getParametersByRequired( boolean required, List<Parameter> parameterList ) 825 { 826 List<Parameter> list = new ArrayList<>(); 827 828 for ( Parameter parameter : parameterList ) 829 { 830 if ( parameter.isRequired() == required ) 831 { 832 list.add( parameter ); 833 } 834 } 835 836 return list; 837 } 838 839 /** 840 * Gets the resource bundle for the <code>locale</code> instance variable. 841 * 842 * @return The resource bundle for the <code>locale</code> instance variable. 843 */ 844 private ResourceBundle getBundle() 845 { 846 return ResourceBundle.getBundle( "pluginxdoc", locale, getClass().getClassLoader() ); 847 } 848 849 /** 850 * @param key not null 851 * @return Localized, text identified by <code>key</code>. 852 * @see #getBundle() 853 */ 854 private String getString( String key ) 855 { 856 return getBundle().getString( key ); 857 } 858 859 /** 860 * Convenience method. 861 * 862 * @param key not null 863 * @param arg1 not null 864 * @return Localized, formatted text identified by <code>key</code>. 865 * @see #format(String, Object[]) 866 */ 867 private String format( String key, Object arg1 ) 868 { 869 return format( key, new Object[]{ arg1 } ); 870 } 871 872 /** 873 * Looks up the value for <code>key</code> in the <code>ResourceBundle</code>, 874 * then formats that value for the specified <code>Locale</code> using <code>args</code>. 875 * 876 * @param key not null 877 * @param args not null 878 * @return Localized, formatted text identified by <code>key</code>. 879 */ 880 private String format( String key, Object[] args ) 881 { 882 String pattern = getString( key ); 883 // we don't need quoting so spare us the confusion in the resource bundle to double them up in some keys 884 pattern = StringUtils.replace( pattern, "'", "''" ); 885 886 MessageFormat messageFormat = new MessageFormat( "" ); 887 messageFormat.setLocale( locale ); 888 messageFormat.applyPattern( pattern ); 889 890 return messageFormat.format( args ); 891 } 892 893 /** 894 * @param text the string to escape 895 * @return A string escaped with XML entities 896 */ 897 private String escapeXml( String text ) 898 { 899 if ( text != null ) 900 { 901 text = text.replace( "&", "&" ); 902 text = text.replace( "<", "<" ); 903 text = text.replace( ">", ">" ); 904 text = text.replace( "\"", """ ); 905 text = text.replace( "\'", "'" ); 906 } 907 return text; 908 } 909 910 String getXhtmlWithValidatedLinks( String xhtmlText, String context ) 911 { 912 if ( disableInternalJavadocLinkValidation ) 913 { 914 return xhtmlText; 915 } 916 StringBuffer sanitizedXhtmlText = new StringBuffer(); 917 // find all links which are not absolute 918 Matcher matcher = HTML_LINK_PATTERN.matcher( xhtmlText ); 919 while ( matcher.find() ) 920 { 921 URI link; 922 try 923 { 924 link = new URI( matcher.group( 1 ) ); 925 if ( !link.isAbsolute() && !JavadocLinkGenerator.isLinkValid( link, reportOutputDirectory.toPath() ) ) 926 { 927 matcher.appendReplacement( sanitizedXhtmlText, matcher.group( 2 ) ); 928 LOG.debug( "Removed invalid link {} in {}", link, context ); 929 } 930 else 931 { 932 matcher.appendReplacement( sanitizedXhtmlText, matcher.group( 0 ) ); 933 } 934 } 935 catch ( URISyntaxException e ) 936 { 937 LOG.warn( "Invalid URI {} found in {}. Cannot validate, leave untouched", matcher.group( 1 ), context ); 938 matcher.appendReplacement( sanitizedXhtmlText, matcher.group( 0 ) ); 939 } 940 } 941 matcher.appendTail( sanitizedXhtmlText ); 942 return sanitizedXhtmlText.toString(); 943 } 944}