View Javadoc

1   package org.apache.maven.plugin.changes;
2   
3   import java.util.ArrayList;
4   import java.util.Iterator;
5   import java.util.List;
6   
7   import org.apache.maven.plugin.AbstractMojo;
8   import org.apache.maven.plugin.MojoExecutionException;
9   
10  /**
11   * Display help information on maven-changes-plugin.<br/> Call <pre>  mvn changes:help -Ddetail=true -Dgoal=&lt;goal-name&gt;</pre> to display parameter details.
12   *
13   * @version generated on Fri May 20 22:21:50 CEST 2011
14   * @author org.apache.maven.tools.plugin.generator.PluginHelpGenerator (version 2.7)
15   * @goal help
16   * @requiresProject false
17   * @threadSafe
18   */
19  public class HelpMojo
20      extends AbstractMojo
21  {
22      /**
23       * If <code>true</code>, display all settable properties for each goal.
24       * 
25       * @parameter expression="${detail}" default-value="false"
26       */
27      private boolean detail;
28  
29      /**
30       * The name of the goal for which to show help. If unspecified, all goals will be displayed.
31       * 
32       * @parameter expression="${goal}"
33       */
34      private java.lang.String goal;
35  
36      /**
37       * The maximum length of a display line, should be positive.
38       * 
39       * @parameter expression="${lineLength}" default-value="80"
40       */
41      private int lineLength;
42  
43      /**
44       * The number of spaces per indentation level, should be positive.
45       * 
46       * @parameter expression="${indentSize}" default-value="2"
47       */
48      private int indentSize;
49  
50  
51      /** {@inheritDoc} */
52      public void execute()
53          throws MojoExecutionException
54      {
55          if ( lineLength <= 0 )
56          {
57              getLog().warn( "The parameter 'lineLength' should be positive, using '80' as default." );
58              lineLength = 80;
59          }
60          if ( indentSize <= 0 )
61          {
62              getLog().warn( "The parameter 'indentSize' should be positive, using '2' as default." );
63              indentSize = 2;
64          }
65  
66          StringBuffer sb = new StringBuffer();
67  
68          append( sb, "org.apache.maven.plugins:maven-changes-plugin:2.5", 0 );
69          append( sb, "", 0 );
70  
71          append( sb, "Maven Changes Report Plugin", 0 );
72          append( sb, "Creates a release history for inclusion into the site and assists in generating an announcement mail.", 1 );
73          append( sb, "", 0 );
74  
75          if ( goal == null || goal.length() <= 0 )
76          {
77              append( sb, "This plugin has 8 goals:", 0 );
78              append( sb, "", 0 );
79          }
80  
81          if ( goal == null || goal.length() <= 0 || "announcement-generate".equals( goal ) )
82          {
83              append( sb, "changes:announcement-generate", 0 );
84              append( sb, "Goal which generate the template for an announcement.", 1 );
85              append( sb, "", 0 );
86              if ( detail )
87              {
88                  append( sb, "Available parameters:", 1 );
89                  append( sb, "", 0 );
90  
91                  append( sb, "announcementFile", 2 );
92                  append( sb, "The name of the file which will contain the generated announcement. If no value is specified the plugin will use the name of the template.", 3 );
93                  append( sb, "Expression: ${changes.announcementFile}", 3 );
94                  append( sb, "", 0 );
95  
96                  append( sb, "announceParameters", 2 );
97                  append( sb, "Map of custom parameters for the announcement. This Map will be passed to the template.", 3 );
98                  append( sb, "", 0 );
99  
100                 append( sb, "basedir", 2 );
101                 append( sb, "The current project base directory.", 3 );
102                 append( sb, "Required: Yes", 3 );
103                 append( sb, "Expression: ${basedir}", 3 );
104                 append( sb, "", 0 );
105 
106                 append( sb, "developmentTeam (Default: ${project.name} team)", 2 );
107                 append( sb, "Name of the team that develops the artifact. This parameter will be passed to the template.", 3 );
108                 append( sb, "Required: Yes", 3 );
109                 append( sb, "Expression: ${changes.developmentTeam}", 3 );
110                 append( sb, "", 0 );
111 
112                 append( sb, "filter", 2 );
113                 append( sb, "Defines the filter parameters to restrict which issues are retrieved from JIRA. The filter parameter uses the same format of url parameters that is used in a JIRA search.", 3 );
114                 append( sb, "", 0 );
115 
116                 append( sb, "finalName (Default: ${project.build.finalName})", 2 );
117                 append( sb, "The name of the artifact to be used in the announcement.", 3 );
118                 append( sb, "Required: Yes", 3 );
119                 append( sb, "Expression: ${changes.finalName}", 3 );
120                 append( sb, "", 0 );
121 
122                 append( sb, "generateJiraAnnouncement (Default: false)", 2 );
123                 append( sb, "Deprecated. Since version 2.4 this parameter has been deprecated. Please use the issueManagementSystems parameter instead.", 3 );
124                 append( sb, "", 0 );
125                 append( sb, "Flag to determine if the plugin will generate a JIRA announcement.", 3 );
126                 append( sb, "Required: Yes", 3 );
127                 append( sb, "Expression: ${generateJiraAnnouncement}", 3 );
128                 append( sb, "", 0 );
129 
130                 append( sb, "introduction (Default: ${project.description})", 2 );
131                 append( sb, "Short description or introduction of the released artifact. This parameter will be passed to the template.", 3 );
132                 append( sb, "", 0 );
133 
134                 append( sb, "issueManagementSystems", 2 );
135                 append( sb, "A list of issue management systems to fetch releases from. This parameter replaces the parameters generateJiraAnnouncement and jiraMerge.\nValid values are: changes.xml and JIRA.\nNote: Only one issue management system that is configured in <project>/<issueManagement> can be used. This currently means that you can combine a changes.xml file with one other issue management system.", 3 );
136                 append( sb, "", 0 );
137 
138                 append( sb, "jiraMerge (Default: false)", 2 );
139                 append( sb, "Deprecated. Since version 2.4 this parameter has been deprecated. Please use the issueManagementSystems parameter instead.", 3 );
140                 append( sb, "", 0 );
141                 append( sb, "If releases from JIRA should be merged with the releases from a changes.xml file.", 3 );
142                 append( sb, "Expression: ${changes.jiraMerge}", 3 );
143                 append( sb, "", 0 );
144 
145                 append( sb, "jiraPassword", 2 );
146                 append( sb, "Defines the JIRA password for authentication into a private JIRA installation.", 3 );
147                 append( sb, "Expression: ${changes.jiraPassword}", 3 );
148                 append( sb, "", 0 );
149 
150                 append( sb, "jiraUser", 2 );
151                 append( sb, "Defines the JIRA username for authentication into a private JIRA installation.", 3 );
152                 append( sb, "Expression: ${changes.jiraUser}", 3 );
153                 append( sb, "", 0 );
154 
155                 append( sb, "maxEntries (Default: 25)", 2 );
156                 append( sb, "The maximum number of issues to fetch from JIRA.\nNote: In versions 2.0-beta-3 and earlier this parameter was called \'nbEntries\'.\n", 3 );
157                 append( sb, "Required: Yes", 3 );
158                 append( sb, "Expression: ${changes.maxEntries}", 3 );
159                 append( sb, "", 0 );
160 
161                 append( sb, "outputDirectory", 2 );
162                 append( sb, "Directory where the template file will be generated.", 3 );
163                 append( sb, "Required: Yes", 3 );
164                 append( sb, "Expression: ${project.build.directory}/announcement", 3 );
165                 append( sb, "", 0 );
166 
167                 append( sb, "resolutionIds (Default: Fixed)", 2 );
168                 append( sb, "Include issues from JIRA with these resolution ids. Multiple resolution ids can be specified as a comma separated list of ids.\nNote: In versions 2.0-beta-3 and earlier this parameter was called \'resolutionId\'.\n", 3 );
169                 append( sb, "Expression: ${changes.resolutionIds}", 3 );
170                 append( sb, "", 0 );
171 
172                 append( sb, "runOnlyAtExecutionRoot (Default: false)", 2 );
173                 append( sb, "This will cause the execution to be run only at the top of a given module tree. That is, run in the project contained in the same folder where the mvn execution was launched.", 3 );
174                 append( sb, "Expression: ${announcement.runOnlyAtExecutionRoot}", 3 );
175                 append( sb, "", 0 );
176 
177                 append( sb, "statusIds (Default: Closed)", 2 );
178                 append( sb, "Include issues from JIRA with these status ids. Multiple status ids can be specified as a comma separated list of ids.\nNote: In versions 2.0-beta-3 and earlier this parameter was called \'statusId\'.\n", 3 );
179                 append( sb, "Expression: ${changes.statusIds}", 3 );
180                 append( sb, "", 0 );
181 
182                 append( sb, "template (Default: announcement.vm)", 2 );
183                 append( sb, "The Velocity template used to format the announcement.", 3 );
184                 append( sb, "Required: Yes", 3 );
185                 append( sb, "Expression: ${changes.template}", 3 );
186                 append( sb, "", 0 );
187 
188                 append( sb, "templateDirectory (Default: org/apache/maven/plugin/announcement)", 2 );
189                 append( sb, "Directory that contains the template.\nNote: This directory must be a subdirectory of /src/main/resources/ or current project base directory.\n", 3 );
190                 append( sb, "Required: Yes", 3 );
191                 append( sb, "Expression: ${changes.templateDirectory}", 3 );
192                 append( sb, "", 0 );
193 
194                 append( sb, "templateEncoding (Default: ${project.build.sourceEncoding})", 2 );
195                 append( sb, "The template encoding.", 3 );
196                 append( sb, "Expression: ${changes.templateEncoding}", 3 );
197                 append( sb, "", 0 );
198 
199                 append( sb, "tracPassword", 2 );
200                 append( sb, "Defines the Trac password for authentication into a private Trac installation.", 3 );
201                 append( sb, "Expression: ${changes.tracPassword}", 3 );
202                 append( sb, "", 0 );
203 
204                 append( sb, "tracQuery (Default: order=id)", 2 );
205                 append( sb, "Defines the Trac query for searching for tickets.", 3 );
206                 append( sb, "", 0 );
207 
208                 append( sb, "tracUser", 2 );
209                 append( sb, "Defines the Trac username for authentication into a private Trac installation.", 3 );
210                 append( sb, "Expression: ${changes.tracUser}", 3 );
211                 append( sb, "", 0 );
212 
213                 append( sb, "url", 2 );
214                 append( sb, "Distribution URL of the artifact. This parameter will be passed to the template.", 3 );
215                 append( sb, "Expression: ${project.url}", 3 );
216                 append( sb, "", 0 );
217 
218                 append( sb, "urlDownload", 2 );
219                 append( sb, "URL where the artifact can be downloaded. If not specified, no URL is used. This parameter will be passed to the template.", 3 );
220                 append( sb, "", 0 );
221 
222                 append( sb, "version (Default: ${project.version})", 2 );
223                 append( sb, "Version of the artifact.", 3 );
224                 append( sb, "Required: Yes", 3 );
225                 append( sb, "Expression: ${changes.version}", 3 );
226                 append( sb, "", 0 );
227 
228                 append( sb, "versionPrefix", 2 );
229                 append( sb, "The prefix used when naming versions in JIRA.\nIf you have a project in JIRA with several components that have different release cycles, it is an often used pattern to prefix the version with the name of the component, e.g. maven-filtering-1.0 etc. To fetch issues from JIRA for a release of the \'maven-filtering\' component you would need to set this parameter to \'maven-filtering-\'.\n", 3 );
230                 append( sb, "", 0 );
231 
232                 append( sb, "webPassword", 2 );
233                 append( sb, "Defines the http password for basic authentication into the JIRA webserver.", 3 );
234                 append( sb, "", 0 );
235 
236                 append( sb, "webUser", 2 );
237                 append( sb, "Defines the http user for basic authentication into the JIRA webserver.", 3 );
238                 append( sb, "", 0 );
239 
240                 append( sb, "xmlPath", 2 );
241                 append( sb, "The path of the changes.xml file.", 3 );
242                 append( sb, "Required: Yes", 3 );
243                 append( sb, "Expression: ${basedir}/src/changes/changes.xml", 3 );
244                 append( sb, "", 0 );
245             }
246         }
247 
248         if ( goal == null || goal.length() <= 0 || "announcement-mail".equals( goal ) )
249         {
250             append( sb, "changes:announcement-mail", 0 );
251             append( sb, "Goal which sends an announcement through email.", 1 );
252             append( sb, "", 0 );
253             if ( detail )
254             {
255                 append( sb, "Available parameters:", 1 );
256                 append( sb, "", 0 );
257 
258                 append( sb, "basedir", 2 );
259                 append( sb, "The current project base directory.", 3 );
260                 append( sb, "Required: Yes", 3 );
261                 append( sb, "Expression: ${basedir}", 3 );
262                 append( sb, "", 0 );
263 
264                 append( sb, "bccAddresses", 2 );
265                 append( sb, "Recipient bcc email address.", 3 );
266                 append( sb, "Required: Yes", 3 );
267                 append( sb, "", 0 );
268 
269                 append( sb, "ccAddresses", 2 );
270                 append( sb, "Recipient cc email address.", 3 );
271                 append( sb, "Required: Yes", 3 );
272                 append( sb, "", 0 );
273 
274                 append( sb, "fromDeveloperId", 2 );
275                 append( sb, "The id of the developer sending the announcement mail. Only used if the mailSender attribute is not set. In this case, this should match the id of one of the developers in the pom. If a matching developer is not found, then the first developer in the pom will be used.", 3 );
276                 append( sb, "Expression: ${changes.fromDeveloperId}", 3 );
277                 append( sb, "", 0 );
278 
279                 append( sb, "mailContentType (Default: text/plain)", 2 );
280                 append( sb, "Mail content type to use.", 3 );
281                 append( sb, "Required: Yes", 3 );
282                 append( sb, "", 0 );
283 
284                 append( sb, "mailSender", 2 );
285                 append( sb, "Defines the sender of the announcement if the list of developer is empty or if the sender is not a member of the development team.", 3 );
286                 append( sb, "Expression: ${changes.mailSender}", 3 );
287                 append( sb, "", 0 );
288 
289                 append( sb, "password", 2 );
290                 append( sb, "The password used to send the email.", 3 );
291                 append( sb, "Expression: ${changes.password}", 3 );
292                 append( sb, "", 0 );
293 
294                 append( sb, "runOnlyAtExecutionRoot (Default: false)", 2 );
295                 append( sb, "This will cause the execution to be run only at the top of a given module tree. That is, run in the project contained in the same folder where the mvn execution was launched.", 3 );
296                 append( sb, "Expression: ${announcement.runOnlyAtExecutionRoot}", 3 );
297                 append( sb, "", 0 );
298 
299                 append( sb, "smtpHost", 2 );
300                 append( sb, "Smtp Server.", 3 );
301                 append( sb, "Required: Yes", 3 );
302                 append( sb, "Expression: ${changes.smtpHost}", 3 );
303                 append( sb, "", 0 );
304 
305                 append( sb, "smtpPort (Default: 25)", 2 );
306                 append( sb, "Port.", 3 );
307                 append( sb, "Required: Yes", 3 );
308                 append( sb, "Expression: ${changes.smtpPort}", 3 );
309                 append( sb, "", 0 );
310 
311                 append( sb, "sslMode (Default: false)", 2 );
312                 append( sb, "If the email should be sent in SSL mode.", 3 );
313                 append( sb, "Expression: ${changes.sslMode}", 3 );
314                 append( sb, "", 0 );
315 
316                 append( sb, "subject (Default: [ANNOUNCEMENT] - ${project.name} ${project.version} released)", 2 );
317                 append( sb, "Subject for the email.", 3 );
318                 append( sb, "Required: Yes", 3 );
319                 append( sb, "Expression: ${changes.subject}", 3 );
320                 append( sb, "", 0 );
321 
322                 append( sb, "template (Default: announcement.vm)", 2 );
323                 append( sb, "The Velocity template used to format the announcement.", 3 );
324                 append( sb, "Required: Yes", 3 );
325                 append( sb, "Expression: ${changes.template}", 3 );
326                 append( sb, "", 0 );
327 
328                 append( sb, "templateOutputDirectory", 2 );
329                 append( sb, "Directory which contains the template for announcement email.", 3 );
330                 append( sb, "Required: Yes", 3 );
331                 append( sb, "Expression: ${project.build.directory}/announcement", 3 );
332                 append( sb, "", 0 );
333 
334                 append( sb, "toAddresses", 2 );
335                 append( sb, "Recipient email address.", 3 );
336                 append( sb, "Required: Yes", 3 );
337                 append( sb, "", 0 );
338 
339                 append( sb, "username", 2 );
340                 append( sb, "The username used to send the email.", 3 );
341                 append( sb, "Expression: ${changes.username}", 3 );
342                 append( sb, "", 0 );
343             }
344         }
345 
346         if ( goal == null || goal.length() <= 0 || "changes-check".equals( goal ) )
347         {
348             append( sb, "changes:changes-check", 0 );
349             append( sb, "Goal which checks that the changes.xml file has the necessary data to generate an announcement or a report for the current release.", 1 );
350             append( sb, "", 0 );
351             if ( detail )
352             {
353                 append( sb, "Available parameters:", 1 );
354                 append( sb, "", 0 );
355 
356                 append( sb, "releaseDateFormat (Default: yyyy-MM-dd)", 2 );
357                 append( sb, "The format that a correct release date should have. This value will be used as a pattern to try to create a date.", 3 );
358                 append( sb, "Expression: ${changes.releaseDateFormat}", 3 );
359                 append( sb, "", 0 );
360 
361                 append( sb, "version (Default: ${project.version})", 2 );
362                 append( sb, "Version of the artifact.", 3 );
363                 append( sb, "Required: Yes", 3 );
364                 append( sb, "Expression: ${changes.version}", 3 );
365                 append( sb, "", 0 );
366 
367                 append( sb, "xmlPath (Default: src/changes/changes.xml)", 2 );
368                 append( sb, "The path of the changes.xml file that will be checked.", 3 );
369                 append( sb, "Expression: ${changes.xmlPath}", 3 );
370                 append( sb, "", 0 );
371             }
372         }
373 
374         if ( goal == null || goal.length() <= 0 || "changes-report".equals( goal ) )
375         {
376             append( sb, "changes:changes-report", 0 );
377             append( sb, "Goal which creates a nicely formatted Changes Report in html format from a changes.xml file.", 1 );
378             append( sb, "", 0 );
379             if ( detail )
380             {
381                 append( sb, "Available parameters:", 1 );
382                 append( sb, "", 0 );
383 
384                 append( sb, "addActionDate (Default: false)", 2 );
385                 append( sb, "A flag whether the report should also include the dates of individual actions. If set to false, only the dates of releases will be written to the report.", 3 );
386                 append( sb, "Expression: ${changes.addActionDate}", 3 );
387                 append( sb, "", 0 );
388 
389                 append( sb, "escapeHTML (Default: true)", 2 );
390                 append( sb, "Deprecated. using markup inside CDATA sections does not work for all output formats!", 3 );
391                 append( sb, "", 0 );
392                 append( sb, "Whether HTML code within an action should be escaped. By changing this to false you can restore the behavior that was in version 2.2 of this plugin, allowing you to use HTML code to format the content of an action.\nNote: If you use HTML code in an action you need to place it inside a CDATA section.\nNote: Putting any kind of markup inside a CDATA section might mess up the Changes Report or other generated documents, such as PDFs, that are based on your changes.xml file if you are not careful.", 3 );
393                 append( sb, "", 0 );
394 
395                 append( sb, "filteringChanges (Default: false)", 2 );
396                 append( sb, "applying filtering filtering \'a la\' resources plugin", 3 );
397                 append( sb, "", 0 );
398 
399                 append( sb, "issueLinkTemplate (Default: %URL%/ViewIssue.jspa?key=%ISSUE%)", 2 );
400                 append( sb, "Deprecated. As of 2.1 use issueLinkTemplatePerSystem : this one will be with system default", 3 );
401                 append( sb, "", 0 );
402                 append( sb, "Template string that is used to discover the URL to use to display an issue report. There are 2 template tokens you can use. %URL%: this is computed by getting the <issueManagement>/<url> value from the POM, and removing the last \'/\' and everything that comes after it. %ISSUE%: this is the issue number.\nNote: In versions of this plugin prior to 2.0-beta-2 this parameter was called link_template.\n", 3 );
403                 append( sb, "Expression: ${changes.issueLinkTemplate}", 3 );
404                 append( sb, "", 0 );
405 
406                 append( sb, "issueLinkTemplatePerSystem", 2 );
407                 append( sb, "Template strings per system that is used to discover the URL to use to display an issue report. Each key in this map denotes the (case-insensitive) identifier of the issue tracking system and its value gives the URL template.\nThere are 2 template tokens you can use. %URL%: this is computed by getting the <issueManagement>/<url> value from the POM, and removing the last \'/\' and everything that comes after it. %ISSUE%: this is the issue number.\n\nNote: The deprecated issueLinkTemplate will be used for a system called \'default\'.\n\nNote: Starting with version 2.4 you usually don\'t need to specify this, unless you need to link to an issue management system in your Changes report that isn\'t supported out of the box. See the Usage page for more information.\n", 3 );
408                 append( sb, "", 0 );
409 
410                 append( sb, "outputDirectory (Default: ${project.reporting.outputDirectory})", 2 );
411                 append( sb, "Report output directory. Note that this parameter is only relevant if the goal is run from the command line or from the default build lifecycle. If the goal is run indirectly as part of a site generation, the output directory configured in the Maven Site Plugin is used instead.", 3 );
412                 append( sb, "", 0 );
413 
414                 append( sb, "outputEncoding (Default: ${project.reporting.outputEncoding})", 2 );
415                 append( sb, "Report output encoding. Note that this parameter is only relevant if the goal is run from the command line or from the default build lifecycle. If the goal is run indirectly as part of a site generation, the output encoding configured in the Maven Site Plugin is used instead.", 3 );
416                 append( sb, "Expression: ${outputEncoding}", 3 );
417                 append( sb, "", 0 );
418 
419                 append( sb, "publishDateFormat (Default: yyyy-MM-dd)", 2 );
420                 append( sb, "Format to use for publishDate. The value will be available with the following expression ${publishDate}", 3 );
421                 append( sb, "", 0 );
422 
423                 append( sb, "publishDateLocale (Default: en)", 2 );
424                 append( sb, "Locale to use for publishDate when formatting", 3 );
425                 append( sb, "", 0 );
426 
427                 append( sb, "teamlist (Default: team-list.html)", 2 );
428                 append( sb, "The URI of a file containing all the team members. If this is set to the special value \'none\', no links will be generated for the team members.", 3 );
429                 append( sb, "", 0 );
430 
431                 append( sb, "xmlPath (Default: src/changes/changes.xml)", 2 );
432                 append( sb, "The path of the changes.xml file that will be converted into an HTML report.", 3 );
433                 append( sb, "Expression: ${changes.xmlPath}", 3 );
434                 append( sb, "", 0 );
435             }
436         }
437 
438         if ( goal == null || goal.length() <= 0 || "changes-validate".equals( goal ) )
439         {
440             append( sb, "changes:changes-validate", 0 );
441             append( sb, "Goal which validate the changes.xml file.", 1 );
442             append( sb, "", 0 );
443             if ( detail )
444             {
445                 append( sb, "Available parameters:", 1 );
446                 append( sb, "", 0 );
447 
448                 append( sb, "changesXsdVersion (Default: 1.0.0)", 2 );
449                 append( sb, "The changes xsd version.", 3 );
450                 append( sb, "Expression: ${changes.xsdVersion}", 3 );
451                 append( sb, "", 0 );
452 
453                 append( sb, "failOnError (Default: false)", 2 );
454                 append( sb, "Mojo failure if validation failed. If not and validation failed only a warning will be logged.", 3 );
455                 append( sb, "Expression: ${changes.validate.failed}", 3 );
456                 append( sb, "", 0 );
457 
458                 append( sb, "xmlPath (Default: src/changes/changes.xml)", 2 );
459                 append( sb, "The path of the changes.xml file that will be converted into an HTML report.", 3 );
460                 append( sb, "Expression: ${changes.xmlPath}", 3 );
461                 append( sb, "", 0 );
462             }
463         }
464 
465         if ( goal == null || goal.length() <= 0 || "help".equals( goal ) )
466         {
467             append( sb, "changes:help", 0 );
468             append( sb, "Display help information on maven-changes-plugin.\nCall\n\u00a0\u00a0mvn\u00a0changes:help\u00a0-Ddetail=true\u00a0-Dgoal=<goal-name>\nto display parameter details.", 1 );
469             append( sb, "", 0 );
470             if ( detail )
471             {
472                 append( sb, "Available parameters:", 1 );
473                 append( sb, "", 0 );
474 
475                 append( sb, "detail (Default: false)", 2 );
476                 append( sb, "If true, display all settable properties for each goal.", 3 );
477                 append( sb, "Expression: ${detail}", 3 );
478                 append( sb, "", 0 );
479 
480                 append( sb, "goal", 2 );
481                 append( sb, "The name of the goal for which to show help. If unspecified, all goals will be displayed.", 3 );
482                 append( sb, "Expression: ${goal}", 3 );
483                 append( sb, "", 0 );
484 
485                 append( sb, "indentSize (Default: 2)", 2 );
486                 append( sb, "The number of spaces per indentation level, should be positive.", 3 );
487                 append( sb, "Expression: ${indentSize}", 3 );
488                 append( sb, "", 0 );
489 
490                 append( sb, "lineLength (Default: 80)", 2 );
491                 append( sb, "The maximum length of a display line, should be positive.", 3 );
492                 append( sb, "Expression: ${lineLength}", 3 );
493                 append( sb, "", 0 );
494             }
495         }
496 
497         if ( goal == null || goal.length() <= 0 || "jira-report".equals( goal ) )
498         {
499             append( sb, "changes:jira-report", 0 );
500             append( sb, "Goal which downloads issues from the Issue Tracking System and generates a report.", 1 );
501             append( sb, "", 0 );
502             if ( detail )
503             {
504                 append( sb, "Available parameters:", 1 );
505                 append( sb, "", 0 );
506 
507                 append( sb, "columnNames (Default: Key,Summary,Status,Resolution,Assignee)", 2 );
508                 append( sb, "Sets the names of the columns that you want in the report. The columns will appear in the report in the same order as you specify them here. Multiple values can be separated by commas.\nValid columns are: Assignee, Component, Created, Fix Version, Id, Key, Priority, Reporter, Resolution, Status, Summary, Type, Updated and Version.\n", 3 );
509                 append( sb, "", 0 );
510 
511                 append( sb, "component", 2 );
512                 append( sb, "Sets the component(s) that you want to limit your report to include. Multiple values can be separated by commas (such as 10011,10012). If this is set to empty - that means all components will be included.", 3 );
513                 append( sb, "", 0 );
514 
515                 append( sb, "filter", 2 );
516                 append( sb, "Defines the filter parameters to restrict which issues are retrieved from JIRA. The filter parameter uses the same format of url parameters that is used in a JIRA search.", 3 );
517                 append( sb, "", 0 );
518 
519                 append( sb, "fixVersionIds", 2 );
520                 append( sb, "Sets the fix version id(s) that you want to limit your report to include. These are JIRA\'s internal version ids, NOT the human readable display ones. Multiple fix versions can be separated by commas. If this is set to empty - that means all fix versions will be included.", 3 );
521                 append( sb, "", 0 );
522 
523                 append( sb, "jiraDatePattern (Default: EEE, d MMM yyyy HH:mm:ss Z)", 2 );
524                 append( sb, "The pattern used by dates in the JIRA XML-file. This is used to parse the Created and Updated fields.", 3 );
525                 append( sb, "", 0 );
526 
527                 append( sb, "jiraPassword", 2 );
528                 append( sb, "Defines the JIRA password for authentication into a private JIRA installation.", 3 );
529                 append( sb, "", 0 );
530 
531                 append( sb, "jiraUser", 2 );
532                 append( sb, "Defines the JIRA username for authentication into a private JIRA installation.", 3 );
533                 append( sb, "", 0 );
534 
535                 append( sb, "maxEntries (Default: 100)", 2 );
536                 append( sb, "Maximum number of entries to be fetched from JIRA.", 3 );
537                 append( sb, "", 0 );
538 
539                 append( sb, "onlyCurrentVersion (Default: false)", 2 );
540                 append( sb, "If you only want to show issues for the current version in the report. The current version being used is ${project.version} minus any \'-SNAPSHOT\' suffix.", 3 );
541                 append( sb, "", 0 );
542 
543                 append( sb, "outputDirectory (Default: ${project.reporting.outputDirectory})", 2 );
544                 append( sb, "Report output directory. Note that this parameter is only relevant if the goal is run from the command line or from the default build lifecycle. If the goal is run indirectly as part of a site generation, the output directory configured in the Maven Site Plugin is used instead.", 3 );
545                 append( sb, "", 0 );
546 
547                 append( sb, "outputEncoding (Default: ${project.reporting.outputEncoding})", 2 );
548                 append( sb, "Report output encoding. Note that this parameter is only relevant if the goal is run from the command line or from the default build lifecycle. If the goal is run indirectly as part of a site generation, the output encoding configured in the Maven Site Plugin is used instead.", 3 );
549                 append( sb, "Expression: ${outputEncoding}", 3 );
550                 append( sb, "", 0 );
551 
552                 append( sb, "priorityIds", 2 );
553                 append( sb, "Sets the priority(s) that you want to limit your report to include. Valid statuses are Blocker, Critical, Major, Minor and Trivial. Multiple values can be separated by commas. If this is set to empty - that means all priorities will be included.", 3 );
554                 append( sb, "", 0 );
555 
556                 append( sb, "resolutionIds (Default: Fixed)", 2 );
557                 append( sb, "Sets the resolution(s) that you want to fetch from JIRA. Valid resolutions are: Unresolved, Fixed, Won\'t Fix, Duplicate, Incomplete and Cannot Reproduce. Multiple values can be separated by commas.\nNote: In versions 2.0-beta-3 and earlier this parameter had no default value.\n", 3 );
558                 append( sb, "", 0 );
559 
560                 append( sb, "sortColumnNames (Default: Priority DESC, Created DESC)", 2 );
561                 append( sb, "Sets the column names that you want to sort the report by. Add DESC following the column name to specify descending sequence. For example Fix Version DESC, Type sorts first by the Fix Version in descending order and then by Type in ascending order. By default sorting is done in ascending order, but is possible to specify ASC for consistency. The previous example would then become Fix Version DESC, Type ASC.\nValid columns are: Assignee, Component, Created, Fix Version, Id, Key, Priority, Reporter, Resolution, Status, Summary, Type, Updated and Version.\n\nNote: If you are using JIRA 4 you need to put your sort column names in the reverse order. The handling of this changed between JIRA 3 and JIRA 4. The current default value is suitable for JIRA 3. This may change in the future, so please configure your sort column names in an order that works for your own JIRA version.\n", 3 );
562                 append( sb, "", 0 );
563 
564                 append( sb, "statusIds (Default: Closed)", 2 );
565                 append( sb, "Sets the status(es) that you want to fetch from JIRA. Valid statuses are: Open, In Progress, Reopened, Resolved and Closed. Multiple values can be separated by commas.\nNote: In versions 2.0-beta-3 and earlier this parameter had no default value.\n", 3 );
566                 append( sb, "", 0 );
567 
568                 append( sb, "typeIds", 2 );
569                 append( sb, "Sets the types(s) that you want to limit your report to include. Valid types are: Bug, New Feature, Task, Improvement, Wish, Test and Sub-task. Multiple values can be separated by commas. If this is set to empty - that means all types will be included.", 3 );
570                 append( sb, "", 0 );
571 
572                 append( sb, "versionPrefix", 2 );
573                 append( sb, "The prefix used when naming versions in JIRA.\nIf you have a project in JIRA with several components that have different release cycles, it is an often used pattern to prefix the version with the name of the component, e.g. maven-filtering-1.0 etc. To fetch issues from JIRA for a release of the \'maven-filtering\' component you would need to set this parameter to \'maven-filtering-\'.\n", 3 );
574                 append( sb, "", 0 );
575 
576                 append( sb, "webPassword", 2 );
577                 append( sb, "Defines the http password for basic authentication into the JIRA webserver.", 3 );
578                 append( sb, "", 0 );
579 
580                 append( sb, "webUser", 2 );
581                 append( sb, "Defines the http user for basic authentication into the JIRA webserver.", 3 );
582                 append( sb, "", 0 );
583             }
584         }
585 
586         if ( goal == null || goal.length() <= 0 || "trac-report".equals( goal ) )
587         {
588             append( sb, "changes:trac-report", 0 );
589             append( sb, "Goal which downloads issues from the Issue Tracking System and generates a report.", 1 );
590             append( sb, "", 0 );
591             if ( detail )
592             {
593                 append( sb, "Available parameters:", 1 );
594                 append( sb, "", 0 );
595 
596                 append( sb, "columnNames (Default: Id,Type,Summary,Assignee,Reporter,Priority,Status,Resolution,Created,Updated)", 2 );
597                 append( sb, "Sets the column names that you want to show in the report. The columns will appear in the report in the same order as you specify them here. Multiple values can be separated by commas.\nValid columns are: Assignee, Component, Created, Fix Version, Id, Priority, Reporter, Resolution, Status, Summary, Type and Updated.\n", 3 );
598                 append( sb, "", 0 );
599 
600                 append( sb, "outputDirectory (Default: ${project.reporting.outputDirectory})", 2 );
601                 append( sb, "Report output directory. Note that this parameter is only relevant if the goal is run from the command line or from the default build lifecycle. If the goal is run indirectly as part of a site generation, the output directory configured in the Maven Site Plugin is used instead.", 3 );
602                 append( sb, "", 0 );
603 
604                 append( sb, "outputEncoding (Default: ${project.reporting.outputEncoding})", 2 );
605                 append( sb, "Report output encoding. Note that this parameter is only relevant if the goal is run from the command line or from the default build lifecycle. If the goal is run indirectly as part of a site generation, the output encoding configured in the Maven Site Plugin is used instead.", 3 );
606                 append( sb, "Expression: ${outputEncoding}", 3 );
607                 append( sb, "", 0 );
608 
609                 append( sb, "query (Default: order=id)", 2 );
610                 append( sb, "Defines the Trac query for searching ticket.", 3 );
611                 append( sb, "", 0 );
612 
613                 append( sb, "tracPassword", 2 );
614                 append( sb, "Defines the Trac password for authentication into a private Trac installation.", 3 );
615                 append( sb, "", 0 );
616 
617                 append( sb, "tracUser", 2 );
618                 append( sb, "Defines the Trac username for authentication into a private Trac installation.", 3 );
619                 append( sb, "", 0 );
620             }
621         }
622 
623         if ( getLog().isInfoEnabled() )
624         {
625             getLog().info( sb.toString() );
626         }
627     }
628 
629     /**
630      * <p>Repeat a String <code>n</code> times to form a new string.</p>
631      *
632      * @param str String to repeat
633      * @param repeat number of times to repeat str
634      * @return String with repeated String
635      * @throws NegativeArraySizeException if <code>repeat < 0</code>
636      * @throws NullPointerException if str is <code>null</code>
637      */
638     private static String repeat( String str, int repeat )
639     {
640         StringBuffer buffer = new StringBuffer( repeat * str.length() );
641 
642         for ( int i = 0; i < repeat; i++ )
643         {
644             buffer.append( str );
645         }
646 
647         return buffer.toString();
648     }
649 
650     /** 
651      * Append a description to the buffer by respecting the indentSize and lineLength parameters.
652      * <b>Note</b>: The last character is always a new line.
653      * 
654      * @param sb The buffer to append the description, not <code>null</code>.
655      * @param description The description, not <code>null</code>.
656      * @param indent The base indentation level of each line, must not be negative.
657      */
658     private void append( StringBuffer sb, String description, int indent )
659     {
660         for ( Iterator it = toLines( description, indent, indentSize, lineLength ).iterator(); it.hasNext(); )
661         {
662             sb.append( it.next().toString() ).append( '\n' );
663         }
664     }
665 
666     /** 
667      * Splits the specified text into lines of convenient display length.
668      * 
669      * @param text The text to split into lines, must not be <code>null</code>.
670      * @param indent The base indentation level of each line, must not be negative.
671      * @param indentSize The size of each indentation, must not be negative.
672      * @param lineLength The length of the line, must not be negative.
673      * @return The sequence of display lines, never <code>null</code>.
674      * @throws NegativeArraySizeException if <code>indent < 0</code>
675      */
676     private static List toLines( String text, int indent, int indentSize, int lineLength )
677     {
678         List lines = new ArrayList();
679 
680         String ind = repeat( "\t", indent );
681         String[] plainLines = text.split( "(\r\n)|(\r)|(\n)" );
682         for ( int i = 0; i < plainLines.length; i++ )
683         {
684             toLines( lines, ind + plainLines[i], indentSize, lineLength );
685         }
686 
687         return lines;
688     }
689 
690     /** 
691      * Adds the specified line to the output sequence, performing line wrapping if necessary.
692      * 
693      * @param lines The sequence of display lines, must not be <code>null</code>.
694      * @param line The line to add, must not be <code>null</code>.
695      * @param indentSize The size of each indentation, must not be negative.
696      * @param lineLength The length of the line, must not be negative.
697      */
698     private static void toLines( List lines, String line, int indentSize, int lineLength )
699     {
700         int lineIndent = getIndentLevel( line );
701         StringBuffer buf = new StringBuffer( 256 );
702         String[] tokens = line.split( " +" );
703         for ( int i = 0; i < tokens.length; i++ )
704         {
705             String token = tokens[i];
706             if ( i > 0 )
707             {
708                 if ( buf.length() + token.length() >= lineLength )
709                 {
710                     lines.add( buf.toString() );
711                     buf.setLength( 0 );
712                     buf.append( repeat( " ", lineIndent * indentSize ) );
713                 }
714                 else
715                 {
716                     buf.append( ' ' );
717                 }
718             }
719             for ( int j = 0; j < token.length(); j++ )
720             {
721                 char c = token.charAt( j );
722                 if ( c == '\t' )
723                 {
724                     buf.append( repeat( " ", indentSize - buf.length() % indentSize ) );
725                 }
726                 else if ( c == '\u00A0' )
727                 {
728                     buf.append( ' ' );
729                 }
730                 else
731                 {
732                     buf.append( c );
733                 }
734             }
735         }
736         lines.add( buf.toString() );
737     }
738 
739     /** 
740      * Gets the indentation level of the specified line.
741      * 
742      * @param line The line whose indentation level should be retrieved, must not be <code>null</code>.
743      * @return The indentation level of the line.
744      */
745     private static int getIndentLevel( String line )
746     {
747         int level = 0;
748         for ( int i = 0; i < line.length() && line.charAt( i ) == '\t'; i++ )
749         {
750             level++;
751         }
752         for ( int i = level + 1; i <= level + 4 && i < line.length(); i++ )
753         {
754             if ( line.charAt( i ) == '\t' )
755             {
756                 level++;
757                 break;
758             }
759         }
760         return level;
761     }
762 }