View Javadoc

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.BufferedInputStream;
20  import java.io.File;
21  import java.io.FileInputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.util.ArrayList;
25  import java.util.List;
26  import java.util.ResourceBundle;
27  
28  import org.apache.commons.io.IOUtils;
29  import org.apache.commons.lang.StringUtils;
30  import org.apache.commons.lang.builder.ToStringBuilder;
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  import org.apache.maven.artifact.versioning.ArtifactVersion;
34  import org.eclipse.mylyn.internal.bugzilla.core.BugzillaAttribute;
35  
36  import de.smartics.maven.issues.notes.NotesReader;
37  import de.smartics.maven.issues.util.ReportReference;
38  
39  /**
40   * Configuration to control the rendering process.
41   *
42   * @author <a href="mailto:robert.reiner@smartics.de">Robert Reiner</a>
43   * @version $Revision:591 $
44   */
45  public final class RendererConfig // NOPMD Config has a lot of properties.
46  {
47    // ********************************* Fields *********************************
48  
49    // --- constants ------------------------------------------------------------
50  
51    // --- members --------------------------------------------------------------
52  
53    /**
54     * The message bundle for labels.
55     */
56    private final ResourceBundle bundle;
57  
58    /**
59     * Sets the component(s) that you want to limit your report to include.
60     * Multiple components can be separated by commas. If this is set to empty -
61     * that means all components.
62     */
63    private final String component;
64  
65    /**
66     * The type (probably but not necessarily a user type) that specifies the
67     * information in the issue that is used to group the issues in sections.
68     * <p>
69     * For example a user type <code>ct_type</code> may be defined to specify the
70     * type of an issue. Valid types could be <code>Bug</code>,
71     * <code>New Feature</code>, <code>Improvement</code>, and <code>Task</code>.
72     * </p>
73     */
74    private final String sectionType;
75  
76    /**
77     * The order of the values specified for the {@link #getSectionType() section
78     * type}. Only values specified in this list will be rendered at all.
79     * <p>
80     * Regarding the example given for {@link #getSectionType()}, this list could
81     * define <code>New Feature</code>, <code>Bug</code>, <code>Improvement</code>
82     * . This would render issues tagged as new features in the first, issues
83     * tagged as bugs in the second and issues tagged as improvements in the last
84     * section. Issues tagged as tasks will not be rendered.
85     */
86    private final List<String> sections;
87  
88    /**
89     * The name of the query to execute. If the query name is specified none of
90     * the other query properties is taken into account.
91     */
92    private final String queryName;
93  
94    /**
95     * Lists the columns to be rendered. Each element of this list is a property
96     * of an issue. The identifiers given here must match the ones defined in the
97     * referenced issue management system. E.g. for Bugzilla these are defined in
98     * <code>org.eclipse.mylyn.internal.bugzilla.core.BugzillaAttribute</code>.
99     */
100   private final List<String> columns;
101 
102   /**
103    * Lists the column width to be used to set to the columns. If the value is
104    * <code>0</code> (zero) no width will be set explicitly for that column.
105    */
106   private final List<String> columnWidths;
107 
108   /**
109    * Specifies the index (zero bases) of the column at which the
110    * {@link #getComponent() component information} is to rendered. This option
111    * is only taken into account if the {@link #getComponent() component
112    * property} does not specify exactly one component. Please note that this
113    * cannot be used in named queries.
114    * <p>
115    * This is a handy option for running the report on a multi project. Sub
116    * projects specify exactly one component while the parent project specifies
117    * none. The result with setting an index of e.g. <code>1</code> is that in
118    * each sub project the component is not mentioned while it is on the parent
119    * project (listing all issues of all projects) it is rendered at the second
120    * column.
121    * </p>
122    */
123   private final int includeComponentAtIndex;
124 
125   /**
126    * Specifies the column width for the {@link #getIncludeComponentAtIndex()
127    * includeComponentAtIndex} property. If that property is not set, this
128    * property value is ignored.
129    */
130   private final int includeComponentAtIndexColumnWidth;
131 
132   /**
133    * The value of the flag that indicates whether or not eMail addresses should
134    * be rendered. Rendering eMail addresses may be useful for intranet sites.
135    * Due to spamming it might not be wise to render an eMail address on an
136    * internet server.
137    * <p>
138    * A value of <code>true</code> indicates that the eMail address should be
139    * rendered (e.g. as a mailto anchor in HTML for an assignee name),
140    * <code>false</code> if no eMail address information should be written.
141    * </p>
142    */
143   private final boolean renderEmailAdresses;
144 
145   /**
146    * A range defining the versions of issues to be rendered.
147    */
148   private final ArtifactVersionRange versionRange;
149 
150   /**
151    * On the same page all of the given version type are rendered. For instance
152    * if this value refers to the major version, all versions having the same
153    * major version are rendered. A value of micro implies that only the current
154    * version is to be rendered.
155    */
156   private final VersionType includeOnSamePageAllOfVersion;
157 
158   /**
159    * The current version of the project. This information is used to determine
160    * which issues are targeted prior or later than the current version.
161    */
162   private final ArtifactVersion currentReleaseVersion;
163 
164   /**
165    * The factory to create comparable version instances. This factory is used to
166    * sort issues by their version information.
167    */
168   private final VersionFactory versionFactory;
169 
170   /**
171    * The title to be set in the configuration to be used instead of the one
172    * found in the localized files. This property is used to specify the title
173    * from the configuration and is useful if the user wants to select a specific
174    * set of information retrieved by a specific query and now wants to set a
175    * specific title.
176    * <p>
177    * This value may be <code>null</code> in which case the renderer retrieves a
178    * default value (probably assuming that a release notes report is rendered).
179    * </p>
180    */
181   private final String title;
182 
183   /**
184    * The description to be set in the configuration to be used instead of the
185    * one found in the localized files. This property is used to specify the
186    * description from the configuration and is useful if the user wants to
187    * select a specific set of information retrieved by a specific query and now
188    * wants to set a specific description.
189    * <p>
190    * This value may be <code>null</code> in which case the renderer retrieves a
191    * default value (probably assuming that a release notes report is rendered).
192    * </p>
193    */
194   private final String description;
195 
196   /**
197    * The description file is a <a
198    * href="http://maven.apache.org/doxia/references/xdoc-format.html">XDoc</a>
199    * file to be included as-is into the generated report. It is rendered between
200    * the main header and the report table where the description is normally
201    * written. The description is left out if a description file is given.
202    */
203   private final File descriptionFile;
204 
205   /**
206    * The description to be set in the configuration if no issue matches the
207    * query to be used instead of the one found in the localized files. This
208    * property is used to specify the description from the configuration and is
209    * useful if the user wants to select a specific set of information retrieved
210    * by a specific query and now wants to set a specific description.
211    * <p>
212    * This value may be omitted in which case the renderer retrieves a default
213    * value (probably assuming that a release notes report is rendered).
214    * </p>
215    */
216   private final String noResultsDescription;
217 
218   /**
219    * The text to be added as a footer as raw text.
220    */
221   private final String footerText;
222 
223   /**
224    * The references to former reports.
225    */
226   private final List<ReportReference> reportReferences;
227 
228   // ****************************** Initializer *******************************
229 
230   // ****************************** Constructors ******************************
231 
232   /**
233    * Default constructor.
234    *
235    * @param builder the builder with the information to set.
236    */
237   private RendererConfig(final Builder builder)
238   {
239     this.bundle = builder.bundle;
240     this.component = builder.component;
241     this.sectionType = builder.sectionType;
242     this.sections = builder.sections;
243     this.queryName = builder.queryName;
244     this.columns = builder.columns;
245     this.columnWidths = builder.columnWidths;
246     this.includeOnSamePageAllOfVersion = builder.includeOnSamePageAllOfVersion;
247     this.includeComponentAtIndex = builder.includeComponentAtIndex;
248     this.includeComponentAtIndexColumnWidth =
249         builder.includeComponentAtIndexColumnWidth;
250     this.renderEmailAdresses = builder.renderEmailAdresses;
251     this.versionRange = builder.versionRange;
252     this.currentReleaseVersion = builder.currentReleaseVersion;
253     this.versionFactory = builder.versionFactory;
254     this.title = builder.title;
255     this.description = builder.description;
256     this.noResultsDescription = builder.noResultsDescription;
257     this.footerText = builder.footerText;
258     this.descriptionFile = builder.descriptionFile;
259     this.reportReferences = builder.reportReferences;
260   }
261 
262   // ****************************** Inner Classes *****************************
263 
264   /**
265    * Builder to create instances of type {@link RendererConfig}.
266    */
267   public static final class Builder // NOPMD Builder has a lot of properties.
268   {
269     // ******************************** Fields ********************************
270 
271     /**
272      * Reference to the logger for this class.
273      */
274     private final Log log = LogFactory.getLog(RendererConfig.class);
275 
276     /**
277      * The message bundle for labels.
278      */
279     private ResourceBundle bundle;
280 
281     /**
282      * Sets the component(s) that you want to limit your report to include.
283      * Multiple components can be separated by commas. If this is set to empty -
284      * that means all components.
285      */
286     private String component;
287 
288     /**
289      * The type (probably but not necessarily a user type) that specifies the
290      * information in the issue that is used to group the issues in sections.
291      * <p>
292      * For example a user type <code>ct_type</code> may be defined to specify
293      * the type of an issue. Valid types could be <code>Bug</code>,
294      * <code>New Feature</code>, <code>Improvement</code>, and <code>Task</code>
295      * .
296      * </p>
297      */
298     private String sectionType;
299 
300     /**
301      * The order of the values specified for the {@link #getSectionType()
302      * section type}. Only values specified in this list will be rendered at
303      * all.
304      * <p>
305      * Regarding the example given for {@link #getSectionType()}, this list
306      * could define <code>New Feature</code>, <code>Bug</code>,
307      * <code>Improvement</code> . This would render issues tagged as new
308      * features in the first, issues tagged as bugs in the second and issues
309      * tagged as improvements in the last section. Issues tagged as tasks will
310      * not be rendered.
311      * </p>
312      */
313     private List<String> sections;
314 
315     /**
316      * The name of the query to execute. If the query name is specified none of
317      * the other query properties is taken into account.
318      */
319     private String queryName;
320 
321     /**
322      * Lists the columns to be rendered. Each element of this list is a property
323      * of an issue. The identifiers given here must match the ones defined in
324      * the referenced issue management system. E.g. for Bugzilla these are
325      * defined in
326      * <code>org.eclipse.mylyn.internal.bugzilla.core.BugzillaAttributey</code>.
327      */
328     private List<String> columns;
329 
330     /**
331      * Lists the column width to be used to set to the columns. If the value is
332      * <code>0</code> (zero) no width will be set explicitly for that column.
333      */
334     private List<String> columnWidths;
335 
336     /**
337      * Specifies the index (zero bases) of the column at which the
338      * {@link #getComponent() component information} is to rendered. This option
339      * is only taken into account if the {@link #getComponent() component
340      * property} does not specify exactly one component. Please note that this
341      * cannot be used in named queries.
342      * <p>
343      * This is a handy option for running the report on a multi project. Sub
344      * projects specify exactly one component while the parent project specifies
345      * none. The result with setting an index of e.g. <code>1</code> is that in
346      * each sub project the component is not mentioned while it is on the parent
347      * project (listing all issues of all projects) it is rendered at the second
348      * column.
349      * </p>
350      */
351     private int includeComponentAtIndex;
352 
353     /**
354      * Specifies the column width for the {@link #getIncludeComponentAtIndex()
355      * includeComponentAtIndex} property. If that property is not set, this
356      * property value is ignored.
357      */
358     private int includeComponentAtIndexColumnWidth;
359 
360     /**
361      * The value of the flag that indicates whether or not eMail addresses
362      * should be rendered. Rendering eMail addresses may be useful for intranet
363      * sites. Due to spamming it might not be wise to render an eMail address on
364      * an internet server.
365      * <p>
366      * A value of <code>true</code> indicates that the eMail address should be
367      * rendered (e.g. as a mailto anchor in HTML for an assignee name),
368      * <code>false</code> if no eMail address information should be written.
369      * </p>
370      */
371     private boolean renderEmailAdresses;
372 
373     /**
374      * On the same page all of the given version type are rendered. For instance
375      * if this value refers to the major version, all versions having the same
376      * major version are rendered. A value of micro implies that only the
377      * current version is to be rendered.
378      * <p>
379      * Defaults to {@link VersionType#MAJOR}.
380      * </p>
381      */
382     private VersionType includeOnSamePageAllOfVersion = VersionType.MAJOR;
383 
384     /**
385      * A range defining the versions of issues to be rendered.
386      */
387     private ArtifactVersionRange versionRange;
388 
389     /**
390      * The current version of the project. This information is used to determine
391      * which issues are targeted prior or later than the current version.
392      */
393     private ArtifactVersion currentReleaseVersion;
394 
395     /**
396      * The factory to create comparable version instances. This factory is used
397      * to sort issues by their version information.
398      */
399     private VersionFactory versionFactory;
400 
401     /**
402      * The title to be set in the configuration to be used instead of the one
403      * found in the localized files. This property is used to specify the title
404      * from the configuration and is useful if the user wants to select a
405      * specific set of information retrieved by a specific query and now wants
406      * to set a specific title.
407      * <p>
408      * This value may be <code>null</code> in which case the renderer retrieves
409      * a default value (probably assuming that a release notes report is
410      * rendered).
411      */
412     private String title;
413 
414     /**
415      * The description to be set in the configuration to be used instead of the
416      * one found in the localized files. This property is used to specify the
417      * description from the configuration and is useful if the user wants to
418      * select a specific set of information retrieved by a specific query and
419      * now wants to set a specific description.
420      * <p>
421      * This value may be <code>null</code> in which case the renderer retrieves
422      * a default value (probably assuming that a release notes report is
423      * rendered).
424      * </p>
425      */
426     private String description;
427 
428     /**
429      * The description file is a <a
430      * href="http://maven.apache.org/doxia/references/xdoc-format.html">XDoc</a>
431      * file to be included as-is into the generated report. It is rendered
432      * between the main header and the report table where the description is
433      * normally written. The description is left out if a description file is
434      * given.
435      */
436     private File descriptionFile;
437 
438     /**
439      * The description to be set in the configuration if no issue matches the
440      * query to be used instead of the one found in the localized files. This
441      * property is used to specify the description from the configuration and is
442      * useful if the user wants to select a specific set of information
443      * retrieved by a specific query and now wants to set a specific
444      * description.
445      * <p>
446      * This value may be omitted in which case the renderer retrieves a default
447      * value (probably assuming that a release notes report is rendered).
448      * </p>
449      */
450     private String noResultsDescription;
451 
452     /**
453      * The text to be added as a footer as raw text.
454      */
455     private String footerText;
456 
457     /**
458      * The references to former reports.
459      */
460     private List<ReportReference> reportReferences;
461 
462     // --- constants ----------------------------------------------------------
463 
464     // --- members ------------------------------------------------------------
465 
466     // ***************************** Initializer ******************************
467 
468     // ***************************** Constructors *****************************
469 
470     // ***************************** Inner Classes ****************************
471 
472     // ******************************** Methods *******************************
473 
474     // --- init ---------------------------------------------------------------
475 
476     // --- get&set ------------------------------------------------------------
477 
478     /**
479      * Sets the message bundle for labels.
480      *
481      * @param bundle the message bundle for labels.
482      */
483     public void setBundle(final ResourceBundle bundle)
484     {
485       this.bundle = bundle;
486     }
487 
488     /**
489      * Sets the value for component.
490      * <p>
491      * Sets the component(s) that you want to limit your report to include.
492      * Multiple components can be separated by commas. If this is set to empty -
493      * that means all components.
494      * </p>
495      *
496      * @param component the value for component.
497      */
498     public void setComponent(final String component)
499     {
500       this.component = component;
501     }
502 
503     /**
504      * Sets the type (probably but not necessarily a user type) that specifies
505      * the information in the issue that is used to group the issues in
506      * sections.
507      * <p>
508      * For example a user type <code>ct_type</code> may be defined to specify
509      * the type of an issue. Valid types could be <code>Bug</code>,
510      * <code>New Feature</code>, <code>Improvement</code>, and <code>Task</code>
511      * .
512      * </p>
513      *
514      * @param sectionType the type (probably but not necessarily a user type)
515      *          that specifies the information in the issue that is used to
516      *          group the issues in sections.
517      */
518     public void setSectionType(final String sectionType)
519     {
520       this.sectionType = sectionType;
521     }
522 
523     /**
524      * Sets the order of the values specified for the <code>sectionType</code>.
525      * Only values specified in this list will be rendered at all.
526      * <p>
527      * Regarding the example given for <code>sectionType</code>, this list could
528      * define <code>New Feature</code>, <code>Bug</code>,
529      * <code>Improvement</code>. This would render issues tagged as new features
530      * in the first, issues tagged as bugs in the second and issues tagged as
531      * improvements in the last section. Issues tagged as tasks will not be
532      * rendered.
533      *
534      * @param sections the order of the values specified for the
535      *          <code>sectionType</code>.
536      */
537     public void setSections(final List<String> sections)
538     {
539       this.sections = new ArrayList<String>(sections);
540     }
541 
542     /**
543      * Sets the name of the query to execute. If the query name is specified
544      * none of the other query properties is taken into account.
545      *
546      * @param queryName the name of the query to execute.
547      */
548     public void setQueryName(final String queryName)
549     {
550       this.queryName = queryName;
551     }
552 
553     /**
554      * Sets the value for columns.
555      * <p>
556      * Lists the columns to be rendered. Each element of this list is a property
557      * of an issue. The identifiers given here must match the ones defined in
558      * the referenced issue management system. E.g. for Bugzilla these are
559      * defined in
560      * <code>org.eclipse.mylyn.internal.bugzilla.core.BugzillaAttribute</code>.
561      * </p>
562      *
563      * @param columns the value for columns.
564      */
565     public void setColumns(final List<String> columns)
566     {
567       this.columns = new ArrayList<String>(columns);
568     }
569 
570     /**
571      * Sets the value for columnWidths.
572      * <p>
573      * Lists the column width to be used to set to the columns. If the value is
574      * <code>0</code> (zero) no width will be set explicitly for that column.
575      * </p>
576      *
577      * @param columnWidths the value for columnWidths.
578      */
579     public void setColumnWidths(final List<String> columnWidths)
580     {
581       this.columnWidths = columnWidths;
582     }
583 
584     /**
585      * Sets the value for includeComponentAtIndex.
586      * <p>
587      * Specifies the index (zero bases) of the column at which the
588      * {@link #getComponent() component information} is to rendered. This option
589      * is only taken into account if the {@link #getComponent() component
590      * property} does not specify exactly one component. Please note that this
591      * cannot be used in named queries.
592      * </p>
593      * <p>
594      * This is a handy option for running the report on a multi project. Sub
595      * projects specify exactly one component while the parent project specifies
596      * none. The result with setting an index of e.g. <code>1</code> is that in
597      * each sub project the component is not mentioned while it is on the parent
598      * project (listing all issues of all projects) it is rendered at the second
599      * column.
600      * </p>
601      *
602      * @param includeComponentAtIndex the value for includeComponentAtIndex.
603      */
604     public void setIncludeComponentAtIndex(final int includeComponentAtIndex)
605     {
606       this.includeComponentAtIndex = includeComponentAtIndex;
607     }
608 
609     /**
610      * Sets the value for includeComponentAtIndexColumnWidth.
611      * <p>
612      * Specifies the column width for the {@link #getIncludeComponentAtIndex()
613      * includeComponentAtIndex} property. If that property is not set, this
614      * property value is ignored.
615      * </p>
616      *
617      * @param includeComponentAtIndexColumnWidth the value for
618      *          includeComponentAtIndexColumnWidth.
619      */
620     public void setIncludeComponentAtIndexColumnWidth(
621         final int includeComponentAtIndexColumnWidth)
622     {
623       this.includeComponentAtIndexColumnWidth =
624           includeComponentAtIndexColumnWidth;
625     }
626 
627     /**
628      * Sets the value of the flag that indicates whether or not eMail addresses
629      * should be rendered. Rendering eMail addresses may be useful for intranet
630      * sites. Due to spamming it might not be wise to render an eMail address on
631      * an internet server.
632      * <p>
633      * A value of <code>true</code> indicates that the eMail address should be
634      * rendered (e.g. as a mailto anchor in HTML for an assignee name),
635      * <code>false</code> if no eMail address information should be written.
636      * </p>
637      *
638      * @param renderEmailAdresses the value of the flag that indicates whether
639      *          or not eMail addresses should be rendered.
640      */
641     public void setRenderEmailAdresses(final boolean renderEmailAdresses)
642     {
643       this.renderEmailAdresses = renderEmailAdresses;
644     }
645 
646     /**
647      * Sets the value for includeOnSamePageAll.
648      * <p>
649      * On the same page all of the given version type are rendered. For instance
650      * if this value refers to the major version, all versions having the same
651      * major version are rendered. A value of micro implies that only the
652      * current version is to be rendered.
653      * <p>
654      * Defaults to {@link VersionType#MAJOR}.
655      * </p>
656      *
657      * @param includeOnSamePageAll the value for includeOnSamePageAll.
658      */
659     public void setIncludeOnSamePageAllOfVersion(
660         final VersionType includeOnSamePageAll)
661     {
662       this.includeOnSamePageAllOfVersion = includeOnSamePageAll;
663     }
664 
665     /**
666      * Sets the range defining the versions of issues to be rendered.
667      *
668      * @param versionRange the range defining the versions of issues to be rendered.
669      */
670     public void setVersionRange(final ArtifactVersionRange versionRange)
671     {
672       this.versionRange = versionRange;
673     }
674 
675     /**
676      * Sets the current version of the project. This information is used to
677      * determine which issues are targeted prior or later than the current
678      * version.
679      *
680      * @param currentReleaseVersion the current version of the project.
681      */
682     public void setCurrentReleaseVersion(
683         final ArtifactVersion currentReleaseVersion)
684     {
685       this.currentReleaseVersion = currentReleaseVersion;
686     }
687 
688     /**
689      * Sets the factory to create comparable version instances. This factory is
690      * used to sort issues by their version information.
691      *
692      * @param versionFactory the factory to create comparable version instances.
693      */
694     public void setVersionFactory(final VersionFactory versionFactory)
695     {
696       this.versionFactory = versionFactory;
697     }
698 
699     /**
700      * Sets the title to be set in the configuration to be used instead of the
701      * one found in the localized files. This property is used to specify the
702      * title from the configuration and is useful if the user wants to select a
703      * specific set of information retrieved by a specific query and now wants
704      * to set a specific title.
705      * <p>
706      * This value may be <code>null</code> in which case the renderer retrieves
707      * a default value (probably assuming that a release notes report is
708      * rendered).
709      *
710      * @param title the title to be set in the configuration to be used instead
711      *          of the one found in the localized files.
712      */
713     public void setTitle(final String title)
714     {
715       this.title = title;
716     }
717 
718     /**
719      * Sets the description to be set in the configuration to be used instead of
720      * the one found in the localized files. This property is used to specify
721      * the description from the configuration and is useful if the user wants to
722      * select a specific set of information retrieved by a specific query and
723      * now wants to set a specific description.
724      * <p>
725      * This value may be <code>null</code> in which case the renderer retrieves
726      * a default value (probably assuming that a release notes report is
727      * rendered).
728      * </p>
729      *
730      * @param description the description to be set in the configuration to be
731      *          used instead of the one found in the localized files.
732      */
733     public void setDescription(final String description)
734     {
735       this.description = description;
736     }
737 
738     /**
739      * Sets the description file is a <a
740      * href="http://maven.apache.org/doxia/references/xdoc-format.html"
741      * >XDoc</a> file to be included as-is into the generated report. It is
742      * rendered between the main header and the report table where the
743      * description is normally written. The description is left out if a
744      * description file is given.
745      *
746      * @param descriptionFile the description file is a <a
747      *          href="http://maven.apache.org/doxia/references/xdoc-format.html"
748      *          >XDoc</a> file to include in the report.
749      */
750     public void setDescriptionFile(final File descriptionFile)
751     {
752       this.descriptionFile = descriptionFile;
753     }
754 
755     /**
756      * Sets the description to be set in the configuration if no issue matches
757      * the query to be used instead of the one found in the localized files.
758      * This property is used to specify the description from the configuration
759      * and is useful if the user wants to select a specific set of information
760      * retrieved by a specific query and now wants to set a specific
761      * description.
762      * <p>
763      * This value may be omitted in which case the renderer retrieves a default
764      * value (probably assuming that a release notes report is rendered).
765      * </p>
766      *
767      * @param noResultsDescription the description to be set in the
768      *          configuration if no issue matches the query to be used instead
769      *          of the one found in the localized files.
770      */
771     public void setNoResultsDescription(final String noResultsDescription)
772     {
773       this.noResultsDescription = noResultsDescription;
774     }
775 
776     /**
777      * Sets the text to be added as a footer as raw text.
778      *
779      * @param footerText the text to be added as a footer as raw text.
780      */
781     public void setFooterText(final String footerText)
782     {
783       this.footerText = footerText;
784     }
785 
786     // --- business -----------------------------------------------------------
787 
788     /**
789      * Validates the information passed to the builder and creates in instance
790      * of type {@link RendererConfig}.
791      *
792      * @return the created instance.
793      * @throws IllegalArgumentException if the information passed to the builder
794      *           is invalid to create the instance.
795      */
796     public RendererConfig build() throws IllegalArgumentException
797     {
798       final StringBuilder buffer = new StringBuilder();
799 
800       if (bundle == null)
801       {
802         buffer.append("No resource bundle specified, but required.");
803       }
804       if (currentReleaseVersion == null)
805       {
806         buffer.append("No current release version specified, but required.");
807       }
808       if (versionFactory == null)
809       {
810         buffer.append("No version factory specified, but required.");
811       }
812 
813       checkSections(buffer);
814       checkColumns(buffer);
815 
816       addIncludeComponentAtIndex();
817 
818       if (buffer.length() > 0)
819       {
820         throw new IllegalArgumentException(buffer.toString());
821       }
822       return new RendererConfig(this); // NOPMD
823     }
824 
825     /**
826      * Adds the {@link #setIncludeComponentAtIndex includeComponentAtIndex}
827      * feature. The feature is only added if it is not a named query, the
828      * columns do not yet contain the component information and the property is
829      * correctly set. If the property is not correctly set (out of column's
830      * range), a warning is issued at warn level but else silently ignored.
831      */
832     private void addIncludeComponentAtIndex()
833     {
834       final String componentKey = BugzillaAttribute.COMPONENT.getKey();
835 
836       if (includeComponentAtIndex >= 0 && StringUtils.isEmpty(queryName)
837           && !columns.contains(componentKey) && !isOnlyOneComponentSpecified())
838       {
839         if (includeComponentAtIndex < columns.size())
840         {
841           columns.add(includeComponentAtIndex, componentKey);
842           columnWidths.add(includeComponentAtIndex,
843               String.valueOf(includeComponentAtIndexColumnWidth));
844         }
845         else
846         {
847           if (log.isWarnEnabled())
848           {
849             log.warn("The index specified for 'includeComponentAtIndex' is out of range. It should be between 0 and "
850                      + (columns.size() - 1)
851                      + " but is set to "
852                      + includeComponentAtIndex + "'. The index is ignored.");
853           }
854         }
855       }
856     }
857 
858     /**
859      * Checks if one and only one component is specified.
860      *
861      * @return <code>true</code> if only one component is specified,
862      *         <code>false</code> if no or more than one component is specified.
863      */
864     private boolean isOnlyOneComponentSpecified()
865     {
866       return StringUtils.isNotBlank(component) && component.indexOf(',') == -1;
867     }
868 
869     /**
870      * Checks the sections information.
871      *
872      * @param buffer the buffer to append violation messages.
873      */
874     private void checkSections(final StringBuilder buffer)
875     {
876       if (StringUtils.isBlank(sectionType))
877       {
878         buffer.append("No section type given that selects the"
879                       + " information of an issue to render sections.");
880       }
881       if (sections == null || sections.isEmpty())
882       {
883         buffer.append("No sections provided to specify which issue types"
884                       + " should be rendered to individual sections.");
885       }
886     }
887 
888     /**
889      * Checks the columns information.
890      *
891      * @param buffer the buffer to append violation messages.
892      */
893     private void checkColumns(final StringBuilder buffer)
894     {
895       if (isNullOrEmpty(columns))
896       {
897         buffer.append("No columns provided to specify which issue information"
898                       + " should be rendered in the report tables.");
899       }
900       if (isNullOrEmpty(columnWidths))
901       {
902         columnWidths = new ArrayList<String>(columns.size());
903         for (int i = columns.size(); i > 0; i--)
904         {
905           columnWidths.add("0");
906         }
907       }
908       if (notOfEqualSize(columns, columnWidths))
909       {
910         buffer.append("The length of columns (" + columns.size()
911                       + ") and columnWidths (" + columnWidths.size()
912                       + ") differs.");
913       }
914     }
915 
916     /**
917      * Checks if both lists are of equal size.
918      *
919      * @param list1 the first list.
920      * @param list2 the second list.
921      * @return <code>true</code> if they are not of equal size, false otherwise.
922      */
923     private static boolean notOfEqualSize(final List<?> list1,
924         final List<?> list2)
925     {
926       return list1 != null && list2 != null && list1.size() != list2.size();
927     }
928 
929     /**
930      * Checks if the given list is <code>null</code> or empty.
931      *
932      * @param list the list to check.
933      * @return true of the list is <code>null</code> or empty,
934      *         <code>false</code> otherwise.
935      */
936     private boolean isNullOrEmpty(final List<?> list)
937     {
938       return list == null || list.isEmpty();
939     }
940 
941     /**
942      * Sets the references to former reports.
943      *
944      * @param reportReferences the references to former reports.
945      */
946     public void setReportReferences(final List<ReportReference> reportReferences)
947     {
948       this.reportReferences = reportReferences;
949     }
950 
951     // --- object basics ------------------------------------------------------
952   }
953 
954   // ********************************* Methods ********************************
955 
956   // --- init -----------------------------------------------------------------
957 
958   // --- get&set --------------------------------------------------------------
959 
960   /**
961    * Returns the message bundle for labels.
962    *
963    * @return the message bundle for labels.
964    */
965   public ResourceBundle getBundle()
966   {
967     return bundle;
968   }
969 
970   /**
971    * Returns the value for component.
972    * <p>
973    * Sets the component(s) that you want to limit your report to include.
974    * Multiple components can be separated by commas. If this is set to empty -
975    * that means all components.
976    *
977    * @return the value for component.
978    */
979   public String getComponent()
980   {
981     return component;
982   }
983 
984   /**
985    * Returns the type (probably but not necessarily a user type) that specifies
986    * the information in the issue that is used to group the issues in sections.
987    * <p>
988    * For example a user type <code>ct_type</code> may be defined to specify the
989    * type of an issue. Valid types could be <code>Bug</code>,
990    * <code>New Feature</code>, <code>Improvement</code>, and <code>Task</code> .
991    * </p>
992    *
993    * @return the type (probably but not necessarily a user type) that specifies
994    *         the information in the issue that is used to group the issues in
995    *         sections.
996    */
997   public String getSectionType()
998   {
999     return sectionType;
1000   }
1001 
1002   /**
1003    * Returns the order of the values specified for the {@link #getSectionType()
1004    * section type}. Only values specified in this list will be rendered at all.
1005    * <p>
1006    * Regarding the example given for {@link #getSectionType()}, this list could
1007    * define <code>New Feature</code>, <code>Bug</code>, <code>Improvement</code>
1008    * . This would render issues tagged as new features in the first, issues
1009    * tagged as bugs in the second and issues tagged as improvements in the last
1010    * section. Issues tagged as tasks will not be rendered.
1011    * </p>
1012    *
1013    * @return the order of the values specified for the {@link #getSectionType()
1014    *         section type}.
1015    */
1016   public List<String> getSections()
1017   {
1018     return sections;
1019   }
1020 
1021   /**
1022    * Returns the name of the query to execute. If the query name is specified
1023    * none of the other query properties is taken into account.
1024    *
1025    * @return the name of the query to execute.
1026    */
1027   public String getQueryName()
1028   {
1029     return queryName;
1030   }
1031 
1032   /**
1033    * Returns the value for columns.
1034    * <p>
1035    * Lists the columns to be rendered. Each element of this list is a property
1036    * of an issue. The identifiers given here must match the ones defined in the
1037    * referenced issue management system. E.g. for Bugzilla these are defined in
1038    * <code>org.eclipse.mylyn.internal.bugzilla.core.BugzillaAttribute</code>.
1039    * </p>
1040    *
1041    * @return the value for columns.
1042    */
1043   public List<String> getColumns()
1044   {
1045     return columns;
1046   }
1047 
1048   /**
1049    * Returns the value for columnWidths.
1050    * <p>
1051    * Lists the column width to be used to set to the columns. If the value is
1052    * <code>0</code> (zero) no width will be set explicitly for that column.
1053    * </p>
1054    *
1055    * @return the value for columnWidths.
1056    */
1057   public List<String> getColumnWidths()
1058   {
1059     return columnWidths;
1060   }
1061 
1062   /**
1063    * Returns the value for includeComponentAtIndex.
1064    * <p>
1065    * Specifies the index (zero bases) of the column at which the
1066    * {@link #getComponent() component information} is to rendered. This option
1067    * is only taken into account if the {@link #getComponent() component
1068    * property} does not specify exactly one component. Please note that this
1069    * cannot be used in named queries.
1070    * </p>
1071    * <p>
1072    * This is a handy option for running the report on a multi project. Sub
1073    * projects specify exactly one component while the parent project specifies
1074    * none. The result with setting an index of e.g. <code>1</code> is that in
1075    * each sub project the component is not mentioned while it is on the parent
1076    * project (listing all issues of all projects) it is rendered at the second
1077    * column.
1078    * </p>
1079    *
1080    * @return the value for includeComponentAtIndex.
1081    */
1082   public int getIncludeComponentAtIndex()
1083   {
1084     return includeComponentAtIndex;
1085   }
1086 
1087   /**
1088    * Returns the value for includeComponentAtIndexColumnWidth.
1089    * <p>
1090    * Specifies the column width for the {@link #getIncludeComponentAtIndex()
1091    * includeComponentAtIndex} property. If that property is not set, this
1092    * property value is ignored.
1093    *
1094    * @return the value for includeComponentAtIndexColumnWidth.
1095    */
1096   public int getIncludeComponentAtIndexColumnWidth()
1097   {
1098     return includeComponentAtIndexColumnWidth;
1099   }
1100 
1101   /**
1102    * Returns the value of the flag that indicates whether or not eMail addresses
1103    * should be rendered. Rendering eMail addresses may be useful for intranet
1104    * sites. Due to spamming it might not be wise to render an eMail address on
1105    * an internet server.
1106    * <p>
1107    * A value of <code>true</code> indicates that the eMail address should be
1108    * rendered (e.g. as a mailto anchor in HTML for an assignee name),
1109    * <code>false</code> if no eMail address information should be written.
1110    * </p>
1111    *
1112    * @return the value of the flag that indicates whether or not eMail addresses
1113    *         should be rendered.
1114    */
1115   public boolean isRenderEmailAdresses()
1116   {
1117     return renderEmailAdresses;
1118   }
1119 
1120   /**
1121    * Returns the value for includeOnSamePageAll.
1122    * <p>
1123    * On the same page all of the given version type are rendered. For instance
1124    * if this value refers to the major version, all versions having the same
1125    * major version are rendered. A value of micro implies that only the current
1126    * version is to be rendered.
1127    * <p>
1128    * Defaults to {@link VersionType#MAJOR}.
1129    * </p>
1130    *
1131    * @return the value for includeOnSamePageAll.
1132    */
1133   public VersionType getIncludeOnSamePageAllOfVersion()
1134   {
1135     return includeOnSamePageAllOfVersion;
1136   }
1137 
1138   /**
1139    * Returns a range defining the versions of issues to be rendered.
1140    *
1141    * @return a range defining the versions of issues to be rendered.
1142    */
1143   public ArtifactVersionRange getVersionRange()
1144   {
1145     return versionRange;
1146   }
1147 
1148   /**
1149    * Returns the current version of the project. This information is used to
1150    * determine which issues are targeted prior or later than the current
1151    * version.
1152    *
1153    * @return the current version of the project.
1154    */
1155   public ArtifactVersion getCurrentReleaseVersion()
1156   {
1157     return currentReleaseVersion;
1158   }
1159 
1160   /**
1161    * Returns the factory to create comparable version instances. This factory is
1162    * used to sort issues by their version information.
1163    *
1164    * @return the factory to create comparable version instances.
1165    */
1166   public VersionFactory getVersionFactoryInstance()
1167   {
1168     return versionFactory;
1169   }
1170 
1171   /**
1172    * Returns the title to be set in the configuration to be used instead of the
1173    * one found in the localized files. This property is used to specify the
1174    * title from the configuration and is useful if the user wants to select a
1175    * specific set of information retrieved by a specific query and now wants to
1176    * set a specific title.
1177    * <p>
1178    * This value may be <code>null</code> in which case the renderer retrieves a
1179    * default value (probably assuming that a release notes report is rendered).
1180    *
1181    * @return the title to be set in the configuration to be used instead of the
1182    *         one found in the localized files.
1183    */
1184   public String getTitle()
1185   {
1186     return title;
1187   }
1188 
1189   /**
1190    * Returns the description to be set in the configuration to be used instead
1191    * of the one found in the localized files. This property is used to specify
1192    * the description from the configuration and is useful if the user wants to
1193    * select a specific set of information retrieved by a specific query and now
1194    * wants to set a specific description.
1195    * <p>
1196    * This value may be <code>null</code> in which case the renderer retrieves a
1197    * default value (probably assuming that a release notes report is rendered).
1198    * </p>
1199    *
1200    * @return the description to be set in the configuration to be used instead
1201    *         of the one found in the localized files.
1202    */
1203   public String getDescription()
1204   {
1205     return description;
1206   }
1207 
1208   /**
1209    * Returns the description file is a <a
1210    * href="http://maven.apache.org/doxia/references/xdoc-format.html">XDoc</a>
1211    * file to be included as-is into the generated report. It is rendered between
1212    * the main header and the report table where the description is normally
1213    * written. The description is left out if a description file is given.
1214    *
1215    * @return the description file is a <a
1216    *         href="http://maven.apache.org/doxia/references/xdoc-format.html"
1217    *         >XDoc</a> file to include in the report.
1218    */
1219   public File getDescriptionFile()
1220   {
1221     return descriptionFile;
1222   }
1223 
1224   /**
1225    * Returns the description to be set in the configuration if no issue matches
1226    * the query to be used instead of the one found in the localized files. This
1227    * property is used to specify the description from the configuration and is
1228    * useful if the user wants to select a specific set of information retrieved
1229    * by a specific query and now wants to set a specific description.
1230    * <p>
1231    * This value may be omitted in which case the renderer retrieves a default
1232    * value (probably assuming that a release notes report is rendered).
1233    * </p>
1234    *
1235    * @return the description to be set in the configuration if no issue matches
1236    *         the query to be used instead of the one found in the localized
1237    *         files.
1238    */
1239   public String getNoResultsDescription()
1240   {
1241     return noResultsDescription;
1242   }
1243 
1244   /**
1245    * Returns the text to be added as a footer as raw text.
1246    *
1247    * @return the text to be added as a footer as raw text.
1248    */
1249   public String getFooterText()
1250   {
1251     return footerText;
1252   }
1253 
1254   /**
1255    * Returns the references to former reports.
1256    *
1257    * @return the references to former reports.
1258    */
1259   public List<ReportReference> getReportReferences()
1260   {
1261     return reportReferences;
1262   }
1263 
1264   // --- business -------------------------------------------------------------
1265 
1266   /**
1267    * Reads the body content from the {@link #getDescriptionFile() description
1268    * file}.
1269    *
1270    * @return the content of the body element (without the body tags). If no
1271    *         description file is given, the empty string is returned.
1272    * @throws IOException if the file cannot be parsed to extract the body
1273    *           element content.
1274    */
1275   public String getDescriptionFileBodyContent() throws IOException
1276   {
1277     final File file = getDescriptionFile();
1278 
1279     if (file != null)
1280     {
1281       final NotesReader reader = new NotesReader();
1282       InputStream in = null;
1283       try
1284       {
1285         in = new BufferedInputStream(new FileInputStream(file));
1286         final String content = reader.read(in);
1287         return content;
1288       }
1289       finally
1290       {
1291         IOUtils.closeQuietly(in);
1292       }
1293     }
1294     return StringUtils.EMPTY;
1295   }
1296 
1297   // --- object basics --------------------------------------------------------
1298 
1299   /**
1300    * {@inheritDoc}
1301    *
1302    * @see java.lang.Object#toString()
1303    */
1304   @Override
1305   public String toString()
1306   {
1307     return ToStringBuilder.reflectionToString(this);
1308   }
1309 }