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