1 /* 2 * Copyright 2008-2013 smartics, Kronseder & Reiner GmbH 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package de.smartics.maven.issues; 17 18 import java.io.File; 19 import java.lang.reflect.Constructor; 20 import java.util.List; 21 import java.util.Locale; 22 23 import org.apache.maven.artifact.versioning.ArtifactVersion; 24 import org.apache.maven.doxia.sink.Sink; 25 import org.apache.maven.plugin.logging.Log; 26 import org.apache.maven.reporting.MavenReportException; 27 import org.apache.maven.reporting.MavenReportRenderer; 28 import org.codehaus.plexus.util.StringUtils; 29 import org.eclipse.mylyn.tasks.core.data.TaskData; 30 31 import de.smartics.maven.issues.util.ReportReference; 32 import de.smartics.maven.issues.util.ReportReferenceExtractor; 33 import de.smartics.maven.issues.util.Utils; 34 35 /** 36 * Mojo base implementation to render release reports. 37 * 38 * @author <a href="mailto:robert.reiner@smartics.de">Robert Reiner</a> 39 * @version $Revision:591 $ 40 */ 41 public abstract class AbstractIssuesReportMojo extends 42 AbstractIssuesConnectionMojo 43 { 44 // ********************************* Fields ********************************* 45 46 // --- constants ------------------------------------------------------------ 47 48 // --- members -------------------------------------------------------------- 49 50 /** 51 * The title to be set in the configuration to be used instead of the one 52 * found in the localized files. This property is used to specify the title 53 * from the configuration and is useful if the user wants to select a specific 54 * set of information retrieved by a specific query and now wants to set a 55 * specific title. 56 * <p> 57 * This value may be omitted in which case the renderer retrieves a default 58 * value (probably assuming that a release notes report is rendered). 59 * </p> 60 * 61 * @parameter default-value="" 62 * @since 1.0 63 */ 64 private String title; 65 66 /** 67 * The description to be set in the configuration to be used instead of the 68 * one found in the localized files. This property is used to specify the 69 * description from the configuration and is useful if the user wants to 70 * select a specific set of information retrieved by a specific query and now 71 * wants to set a specific description. 72 * <p> 73 * This value may be omitted in which case the renderer retrieves a default 74 * value (probably assuming that a release notes report is rendered). 75 * </p> 76 * 77 * @parameter default-value="" 78 * @since 1.0 79 */ 80 protected String description; 81 82 /** 83 * The description file is a <a 84 * href="http://maven.apache.org/doxia/references/xdoc-format.html">XDoc</a> 85 * file to be included as-is into the generated report. It is rendered between 86 * the main header and the report table where the description is normally 87 * written. The description is left out if a description file is given. 88 * <p> 89 * If no description file is explicitly specified, the system looks at the 90 * default location. If the file exists, it is used. 91 * </p> 92 * <p> 93 * Please note that any <code>-SNAPSHOT</code> element in front of the 94 * extension is removed. <code>$${outputName}</code> is replaced by 95 * {@link #getOutputName()}. 96 * </p> 97 * 98 * @parameter default-value= 99 * "src/site/relnotes/$${outputName}-${project.version}.xml" 100 * @since 1.0 101 */ 102 protected String descriptionFile; 103 104 /** 105 * The description to be set in the configuration if no issue matches the 106 * query to be used instead of the one found in the localized files. This 107 * property is used to specify the description from the configuration and is 108 * useful if the user wants to select a specific set of information retrieved 109 * by a specific query and now wants to set a specific description. 110 * <p> 111 * This value may be omitted in which case the renderer retrieves a default 112 * value (probably assuming that a release notes report is rendered). 113 * </p> 114 * 115 * @parameter default-value="" 116 * @since 1.0 117 */ 118 private String noResultsDescription; 119 120 /** 121 * The raw text for the footer. May contain any valid HTML code. The string is 122 * not checked to be valid. If you want to remove the link to the plugin's 123 * homepage, simple add a non breaking space (e.g. <code>&amp;nbsp;</code> 124 * ). 125 * 126 * @parameter 127 * @since 1.0 128 */ 129 protected String footerText = 130 "<div style='text-align:center;font-size:x-small;'>generated by " 131 + "<a href='" + "http://project.smartics.de/issues-maven-plugin'>" 132 + "issues-maven-plugin</a></div>"; 133 134 /** 135 * The type (probably but not necessarily a user type) that specifies the 136 * information in the issue that is used to group the issues in sections. 137 * <p> 138 * For example a user type <code>ct_type</code> may be defined to specify the 139 * type of an issue. Valid types could be <code>Bug</code>, 140 * <code>New Feature</code>, <code>Improvement</code>, and <code>Task</code>. 141 * </p> 142 * 143 * @parameter default-value="cf_type" 144 * @since 1.0 145 */ 146 private String sectionType; 147 148 /** 149 * The order of the values specified for the section type. Only values 150 * specified in this list will be rendered at all. 151 * <p> 152 * Regarding the example given for "section type", this list could define 153 * <code>New Feature</code>, <code>Bug</code>, <code>Improvement</code> . This 154 * would render issues tagged as new features in the first, issues tagged as 155 * bugs in the second and issues tagged as improvements in the last section. 156 * Issues tagged as tasks will not be rendered. 157 * </p> 158 * <p> 159 * The values are separated by comma. 160 * </p> 161 * 162 * @parameter default-value="New Feature,Change Request,Improvement,Bug" 163 * @since 1.0 164 */ 165 private String sections; 166 167 /** 168 * The value of the flag that indicates whether or not eMail addresses should 169 * be rendered. Rendering eMail addresses may be useful for intranet sites. 170 * Due to spamming it might not be wise to render an eMail address on an 171 * internet server. 172 * <p> 173 * A value of <code>true</code> indicates that the eMail address should be 174 * rendered (e.g. as a mailto anchor in HTML for an assignee name), 175 * <code>false</code> if no eMail address information should be written. 176 * </p> 177 * 178 * @parameter default-value="false" 179 * @since 1.0 180 */ 181 private boolean renderEmailAdresses; 182 183 /** 184 * Specifies the index (zero bases) of the column at which the 185 * {@link #getComponent() component information} is to rendered. This option 186 * is only taken into account if the {@link #getComponent() component 187 * property} does not specify exactly one component. Please note that this 188 * cannot be used in named queries. 189 * <p> 190 * This is a handy option for running the report on a multi project. Sub 191 * projects specify exactly one component while the parent project specifies 192 * none. The result with setting an index of e.g. <code>1</code> is that in 193 * each sub project the component is not mentioned while it is on the parent 194 * project (listing all issues of all projects) it is rendered at the second 195 * column. 196 * </p> 197 * 198 * @parameter default-value="1" 199 * @since 1.0 200 */ 201 private int includeComponentAtIndex; 202 203 /** 204 * Specifies the column width for the {@link #getIncludeComponentAtIndex() 205 * includeComponentAtIndex} property. If that property is not set, this 206 * property value is ignored. 207 * 208 * @parameter default-value="0" 209 * @since 1.0 210 */ 211 private int includeComponentAtIndexColumnWidth; 212 213 /** 214 * Specifies if previous versions of this report should be referenced. 215 * 216 * @parameter default-value="true" 217 * @since 1.0 218 */ 219 private boolean referencePreviousReports; 220 221 // ****************************** Initializer ******************************* 222 223 // ****************************** Constructors ****************************** 224 225 // ****************************** Inner Classes ***************************** 226 227 // ********************************* Methods ******************************** 228 229 // --- init ----------------------------------------------------------------- 230 231 // --- get&set -------------------------------------------------------------- 232 233 /** 234 * Returns the title to be set in the configuration to be used instead of the 235 * one found in the localized files. This property is used to specify the 236 * title from the configuration and is useful if the user wants to select a 237 * specific set of information retrieved by a specific query and now wants to 238 * set a specific title. 239 * <p> 240 * This value may be omitted in which case the renderer retrieves a default 241 * value (probably assuming that a release notes report is rendered). 242 * </p> 243 * 244 * @return the title to be set in the configuration to be used instead of the 245 * one found in the localized files. 246 */ 247 public String getTitle() 248 { 249 return title; 250 } 251 252 /** 253 * Returns the description to be set in the configuration to be used instead 254 * of the one found in the localized files. This property is used to specify 255 * the description from the configuration and is useful if the user wants to 256 * select a specific set of information retrieved by a specific query and now 257 * wants to set a specific description. 258 * <p> 259 * This value may be omitted in which case the renderer retrieves a default 260 * value (probably assuming that a release notes report is rendered). 261 * </p> 262 * 263 * @return the description to be set in the configuration to be used instead 264 * of the one found in the localized files. 265 */ 266 public String getDescription() 267 { 268 return description; 269 } 270 271 /** 272 * Returns the description file is a <a 273 * href="http://maven.apache.org/doxia/references/xdoc-format.html">XDoc</a> 274 * file to be included as-is into the generated report. It is rendered between 275 * the main header and the report table where the description is normally 276 * written. The description is left out if a description file is given. 277 * 278 * @return the description file is a <a 279 * href="http://maven.apache.org/doxia/references/xdoc-format.html" 280 * >XDoc</a> file to be included as-is into the generated report. 281 */ 282 public String getDescriptionFile() 283 { 284 return descriptionFile; 285 } 286 287 /** 288 * Returns the description to be set in the configuration if no issue matches 289 * the query to be used instead of the one found in the localized files. This 290 * property is used to specify the description from the configuration and is 291 * useful if the user wants to select a specific set of information retrieved 292 * by a specific query and now wants to set a specific description. 293 * <p> 294 * This value may be omitted in which case the renderer retrieves a default 295 * value (probably assuming that a release notes report is rendered). 296 * </p> 297 * 298 * @return the description to be set in the configuration if no issue matches 299 * the query to be used instead of the one found in the localized 300 * files. 301 */ 302 public String getNoResultsDescription() 303 { 304 return noResultsDescription; 305 } 306 307 /** 308 * Returns the name of the class that runs the rendering of the report page. 309 * 310 * @return the name of the class that runs the rendering of the report page. 311 */ 312 protected abstract String getReportRenderer(); 313 314 /** 315 * Returns the value for columns. 316 * <p> 317 * Lists the columns to be rendered. Each element of this list is a property 318 * of an issue. The identifiers given here must match the ones defined in the 319 * referenced issue management system. E.g. for Bugzilla these are defined in 320 * <code>org.eclipse.mylyn.internal.bugzilla.core.BugzillaAttribute</code>. 321 * <p> 322 * The values are separated by comma. 323 * </p> 324 * 325 * @return the value for columns. 326 */ 327 protected abstract String getColumns(); 328 329 /** 330 * Returns the value for columnWidths. 331 * <p> 332 * Lists the column width to be used to set to the columns. If the value is 333 * <code>0</code> (zero) no width will be set explicitly for that column. 334 * </p> 335 * 336 * @return the value for columnWidths. 337 */ 338 protected abstract String getColumnWidths(); 339 340 /** 341 * Returns the value for includeOnSamePageAll. 342 * <p> 343 * On the same page all of the given version type are rendered. For instance 344 * if this value refers to the major version, all versions having the same 345 * major version are rendered. A value of micro implies that only the current 346 * version is to be rendered. 347 * <p> 348 * Defaults to {@link VersionType#MAJOR}. 349 * </p> 350 * 351 * @return the value for includeOnSamePageAll. 352 */ 353 public abstract VersionType getIncludeOnSamePageAllOfVersion(); 354 355 /** 356 * Returns the value for component. 357 * <p> 358 * Sets the component(s) that you want to limit your report to include. 359 * Multiple components can be separated by commas. If this is set to empty - 360 * that means all components. 361 * 362 * @return the value for component. 363 */ 364 public abstract String getComponent(); 365 366 /** 367 * Returns the name of the query to execute. If the query name is specified 368 * none of the other query properties is taken into account. 369 * 370 * @return the name of the query to execute. 371 */ 372 public abstract String getQueryName(); 373 374 /** 375 * Returns the value for includeComponentAtIndex. 376 * <p> 377 * Specifies the index (zero bases) of the column at which the 378 * {@link #getComponent() component information} is to rendered. This option 379 * is only taken into account if the {@link #getComponent() component 380 * property} does not specify exactly one component. Please note that this 381 * cannot be used in named queries. 382 * </p> 383 * <p> 384 * This is a handy option for running the report on a multi project. Sub 385 * projects specify exactly one component while the parent project specifies 386 * none. The result with setting an index of e.g. <code>1</code> is that in 387 * each sub project the component is not mentioned while it is on the parent 388 * project (listing all issues of all projects) it is rendered at the second 389 * column. 390 * </p> 391 * 392 * @return the value for includeComponentAtIndex. 393 */ 394 public int getIncludeComponentAtIndex() 395 { 396 return includeComponentAtIndex; 397 } 398 399 /** 400 * Returns the value for includeComponentAtIndexColumnWidth. 401 * <p> 402 * Specifies the column width for the {@link #getIncludeComponentAtIndex() 403 * includeComponentAtIndex} property. If that property is not set, this 404 * property value is ignored. 405 * </p> 406 * 407 * @return the value for includeComponentAtIndexColumnWidth. 408 */ 409 public int getIncludeComponentAtIndexColumnWidth() 410 { 411 return includeComponentAtIndexColumnWidth; 412 } 413 414 // --- business ------------------------------------------------------------- 415 416 /** 417 * Creates the renderer to use for report rendering. 418 * 419 * @param locale the locale to select the resource bundle that provides labels 420 * for the generated reports. 421 * @param issues the issues to render. 422 * @return the created renderer instance. 423 * @throws MavenReportException if a problem prevents the creation of the 424 * report renderer. 425 */ 426 @Override 427 @SuppressWarnings("unchecked") 428 protected MavenReportRenderer createRenderer(final Locale locale, 429 final VersionFactory versionFactoryInstance, 430 final ArtifactVersionRange versionRange, final List<TaskData> issues) 431 throws MavenReportException 432 { 433 final Log log = getLog(); 434 final String reportRendererName = getReportRenderer(); 435 try 436 { 437 final Class<MavenReportRenderer> clazz = 438 (Class<MavenReportRenderer>) Class.forName(reportRendererName); 439 final Constructor<MavenReportRenderer> constructor = 440 clazz.getConstructor(RendererConfig.class, Sink.class, List.class); 441 442 final RendererConfig config = 443 createRenderConfig(locale, versionFactoryInstance, versionRange); 444 if (log.isDebugEnabled()) 445 { 446 log.debug("Created configuration: " + config); 447 } 448 449 final MavenReportRenderer renderer = 450 constructor.newInstance(config, getSink(), issues); 451 return renderer; 452 } 453 catch (final Exception e) 454 { 455 final String message = 456 "Cannot create renderer for class '" + reportRendererName + "'."; 457 if (log.isWarnEnabled()) 458 { 459 log.warn(message, e); 460 } 461 throw new MavenReportException(message, e); 462 } 463 } 464 465 /** 466 * Creates the renderer configuration from the information set to the Mojo. 467 * 468 * @param locale the locale to select the resource bundle that provides labels 469 * for the generated reports. 470 * @return the configuration instance. 471 * @throws IllegalArgumentException if the information is not valid to create 472 * an instance. 473 */ 474 private RendererConfig createRenderConfig(final Locale locale, 475 final VersionFactory versionFactoryInstance, 476 final ArtifactVersionRange versionRange) throws IllegalArgumentException 477 { 478 final RendererConfig.Builder builder = new RendererConfig.Builder(); 479 builder.setBundle(getBundle(locale)); 480 481 builder.setComponent(getComponent()); 482 builder.setSectionType(sectionType); 483 builder.setSections(Utils.splitToList(sections)); 484 485 builder.setColumns(Utils.splitToList(getColumns())); 486 builder.setColumnWidths(Utils.splitToList(getColumnWidths())); 487 builder.setIncludeComponentAtIndex(getIncludeComponentAtIndex()); 488 builder 489 .setIncludeComponentAtIndexColumnWidth(getIncludeComponentAtIndexColumnWidth()); 490 491 builder.setRenderEmailAdresses(renderEmailAdresses); 492 493 builder.setQueryName(getQueryName()); 494 495 final ArtifactVersion version = 496 addVersionInformation(versionFactoryInstance, versionRange, builder); 497 498 setTitle(builder); 499 setDescription(builder); 500 setDescriptionFile(builder); 501 setNoResultDescription(builder); 502 setFooterText(builder); 503 504 if (referencePreviousReports) 505 { 506 final ReportReferenceExtractor extractor = 507 new ReportReferenceExtractor(getOutputName(), version, 508 project.getReporting()); 509 final List<ReportReference> references = extractor.readReportReferences(); 510 builder.setReportReferences(references); 511 } 512 513 return builder.build(); 514 } 515 516 /** 517 * Adds version information to the builder. 518 * 519 * @param builder the configuration builder to add to. 520 * @return the project's version. 521 * @throws IllegalArgumentException if the version specification cannot be 522 * parsed. 523 */ 524 private ArtifactVersion addVersionInformation( 525 final VersionFactory versionFactoryInstance, 526 final ArtifactVersionRange versionRange, 527 final RendererConfig.Builder builder) throws IllegalArgumentException 528 { 529 builder.setVersionFactory(versionFactoryInstance); 530 builder.setVersionRange(versionRange); 531 builder 532 .setIncludeOnSamePageAllOfVersion(getIncludeOnSamePageAllOfVersion()); 533 final ArtifactVersion artifactVersion = 534 versionFactoryInstance.createVersion(project.getVersion()); 535 builder.setCurrentReleaseVersion(artifactVersion); 536 return artifactVersion; 537 } 538 539 /** 540 * Sets the description to render if the query yielded no result. 541 * 542 * @param builder the builder to add the information. 543 */ 544 private void setNoResultDescription(final RendererConfig.Builder builder) 545 { 546 if (StringUtils.isNotBlank(noResultsDescription)) 547 { 548 builder.setNoResultsDescription(noResultsDescription); 549 } 550 } 551 552 /** 553 * Sets the title to the builder. 554 * 555 * @param builder the builder to add the information. 556 */ 557 private void setTitle(final RendererConfig.Builder builder) 558 { 559 if (StringUtils.isNotBlank(getTitle())) 560 { 561 builder.setTitle(getTitle()); 562 } 563 } 564 565 /** 566 * Sets the description to the builder. 567 * 568 * @param builder the builder to add the information. 569 */ 570 private void setDescription(final RendererConfig.Builder builder) 571 { 572 if (StringUtils.isNotBlank(description)) 573 { 574 builder.setDescription(description); 575 } 576 } 577 578 /** 579 * Sets the description file to the builder. If the file does not exist, the 580 * fact is logged at debug level and no value is set to the configuration 581 * builder. 582 * 583 * @param builder the builder to add the information. 584 */ 585 private void setDescriptionFile(final RendererConfig.Builder builder) 586 { 587 final Log log = getLog(); 588 if (log.isDebugEnabled()) 589 { 590 log.debug("Checking description file: " + descriptionFile); 591 } 592 593 if (StringUtils.isNotBlank(descriptionFile)) 594 { 595 final File file = determineDescriptionFile(); 596 if (file.canRead()) 597 { 598 builder.setDescriptionFile(file); 599 } 600 else 601 { 602 if (log.isDebugEnabled()) 603 { 604 log.debug("Cannot read description file '" + file.getAbsolutePath() 605 + "'."); 606 } 607 } 608 } 609 } 610 611 /** 612 * Determines the location to search for the description file. 613 * 614 * @return the file representation to check. 615 */ 616 private File determineDescriptionFile() 617 { 618 final String normalizedFileName = 619 descriptionFile.replace("-SNAPSHOT.xml", ".xml").replace( 620 "${outputName}", getOutputName()); 621 return new File(project.getBasedir(), normalizedFileName); 622 } 623 624 /** 625 * Sets the footer text to the builder. 626 * 627 * @param builder the builder to add the information. 628 */ 629 private void setFooterText(final RendererConfig.Builder builder) 630 { 631 if (StringUtils.isNotBlank(footerText)) 632 { 633 builder.setFooterText(footerText); 634 } 635 } 636 637 // --- object basics -------------------------------------------------------- 638 639 }