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 Sun Jun 19 15:23:53 EDT 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.6", 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, "issueTypes", 2 );
139                 append( sb, "Maps issues types to action types for grouping issues in announcements. If issue types are not defined for a action type then the default issue type will be applied.\nValid action types: add, fix and update.\n", 3 );
140                 append( sb, "", 0 );
141 
142                 append( sb, "jiraMerge (Default: false)", 2 );
143                 append( sb, "Deprecated. Since version 2.4 this parameter has been deprecated. Please use the issueManagementSystems parameter instead.", 3 );
144                 append( sb, "", 0 );
145                 append( sb, "If releases from JIRA should be merged with the releases from a changes.xml file.", 3 );
146                 append( sb, "Expression: ${changes.jiraMerge}", 3 );
147                 append( sb, "", 0 );
148 
149                 append( sb, "jiraPassword", 2 );
150                 append( sb, "Defines the JIRA password for authentication into a private JIRA installation.", 3 );
151                 append( sb, "Expression: ${changes.jiraPassword}", 3 );
152                 append( sb, "", 0 );
153 
154                 append( sb, "jiraUser", 2 );
155                 append( sb, "Defines the JIRA username for authentication into a private JIRA installation.", 3 );
156                 append( sb, "Expression: ${changes.jiraUser}", 3 );
157                 append( sb, "", 0 );
158 
159                 append( sb, "maxEntries (Default: 25)", 2 );
160                 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 );
161                 append( sb, "Required: Yes", 3 );
162                 append( sb, "Expression: ${changes.maxEntries}", 3 );
163                 append( sb, "", 0 );
164 
165                 append( sb, "outputDirectory", 2 );
166                 append( sb, "Directory where the template file will be generated.", 3 );
167                 append( sb, "Required: Yes", 3 );
168                 append( sb, "Expression: ${project.build.directory}/announcement", 3 );
169                 append( sb, "", 0 );
170 
171                 append( sb, "resolutionIds (Default: Fixed)", 2 );
172                 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 );
173                 append( sb, "Expression: ${changes.resolutionIds}", 3 );
174                 append( sb, "", 0 );
175 
176                 append( sb, "runOnlyAtExecutionRoot (Default: false)", 2 );
177                 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 );
178                 append( sb, "Expression: ${announcement.runOnlyAtExecutionRoot}", 3 );
179                 append( sb, "", 0 );
180 
181                 append( sb, "statusIds (Default: Closed)", 2 );
182                 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 );
183                 append( sb, "Expression: ${changes.statusIds}", 3 );
184                 append( sb, "", 0 );
185 
186                 append( sb, "template (Default: announcement.vm)", 2 );
187                 append( sb, "The Velocity template used to format the announcement.", 3 );
188                 append( sb, "Required: Yes", 3 );
189                 append( sb, "Expression: ${changes.template}", 3 );
190                 append( sb, "", 0 );
191 
192                 append( sb, "templateDirectory (Default: org/apache/maven/plugin/announcement)", 2 );
193                 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 );
194                 append( sb, "Required: Yes", 3 );
195                 append( sb, "Expression: ${changes.templateDirectory}", 3 );
196                 append( sb, "", 0 );
197 
198                 append( sb, "templateEncoding (Default: ${project.build.sourceEncoding})", 2 );
199                 append( sb, "The template encoding.", 3 );
200                 append( sb, "Expression: ${changes.templateEncoding}", 3 );
201                 append( sb, "", 0 );
202 
203                 append( sb, "tracPassword", 2 );
204                 append( sb, "Defines the Trac password for authentication into a private Trac installation.", 3 );
205                 append( sb, "Expression: ${changes.tracPassword}", 3 );
206                 append( sb, "", 0 );
207 
208                 append( sb, "tracQuery (Default: order=id)", 2 );
209                 append( sb, "Defines the Trac query for searching for tickets.", 3 );
210                 append( sb, "", 0 );
211 
212                 append( sb, "tracUser", 2 );
213                 append( sb, "Defines the Trac username for authentication into a private Trac installation.", 3 );
214                 append( sb, "Expression: ${changes.tracUser}", 3 );
215                 append( sb, "", 0 );
216 
217                 append( sb, "url", 2 );
218                 append( sb, "Distribution URL of the artifact. This parameter will be passed to the template.", 3 );
219                 append( sb, "Expression: ${project.url}", 3 );
220                 append( sb, "", 0 );
221 
222                 append( sb, "urlDownload", 2 );
223                 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 );
224                 append( sb, "", 0 );
225 
226                 append( sb, "version (Default: ${project.version})", 2 );
227                 append( sb, "Version of the artifact.", 3 );
228                 append( sb, "Required: Yes", 3 );
229                 append( sb, "Expression: ${changes.version}", 3 );
230                 append( sb, "", 0 );
231 
232                 append( sb, "versionPrefix", 2 );
233                 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 );
234                 append( sb, "", 0 );
235 
236                 append( sb, "webPassword", 2 );
237                 append( sb, "Defines the http password for basic authentication into the JIRA webserver.", 3 );
238                 append( sb, "", 0 );
239 
240                 append( sb, "webUser", 2 );
241                 append( sb, "Defines the http user for basic authentication into the JIRA webserver.", 3 );
242                 append( sb, "", 0 );
243 
244                 append( sb, "xmlPath", 2 );
245                 append( sb, "The path of the changes.xml file.", 3 );
246                 append( sb, "Required: Yes", 3 );
247                 append( sb, "Expression: ${basedir}/src/changes/changes.xml", 3 );
248                 append( sb, "", 0 );
249             }
250         }
251 
252         if ( goal == null || goal.length() <= 0 || "announcement-mail".equals( goal ) )
253         {
254             append( sb, "changes:announcement-mail", 0 );
255             append( sb, "Goal which sends an announcement through email.", 1 );
256             append( sb, "", 0 );
257             if ( detail )
258             {
259                 append( sb, "Available parameters:", 1 );
260                 append( sb, "", 0 );
261 
262                 append( sb, "basedir", 2 );
263                 append( sb, "The current project base directory.", 3 );
264                 append( sb, "Required: Yes", 3 );
265                 append( sb, "Expression: ${basedir}", 3 );
266                 append( sb, "", 0 );
267 
268                 append( sb, "bccAddresses", 2 );
269                 append( sb, "Recipient bcc email address.", 3 );
270                 append( sb, "", 0 );
271 
272                 append( sb, "ccAddresses", 2 );
273                 append( sb, "Recipient cc email address.", 3 );
274                 append( sb, "", 0 );
275 
276                 append( sb, "fromDeveloperId", 2 );
277                 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 );
278                 append( sb, "Expression: ${changes.fromDeveloperId}", 3 );
279                 append( sb, "", 0 );
280 
281                 append( sb, "mailContentType (Default: text/plain)", 2 );
282                 append( sb, "Mail content type to use.", 3 );
283                 append( sb, "Required: Yes", 3 );
284                 append( sb, "", 0 );
285 
286                 append( sb, "mailSender", 2 );
287                 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 );
288                 append( sb, "Expression: ${changes.mailSender}", 3 );
289                 append( sb, "", 0 );
290 
291                 append( sb, "password", 2 );
292                 append( sb, "The password used to send the email.", 3 );
293                 append( sb, "Expression: ${changes.password}", 3 );
294                 append( sb, "", 0 );
295 
296                 append( sb, "runOnlyAtExecutionRoot (Default: false)", 2 );
297                 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 );
298                 append( sb, "Expression: ${announcement.runOnlyAtExecutionRoot}", 3 );
299                 append( sb, "", 0 );
300 
301                 append( sb, "smtpHost", 2 );
302                 append( sb, "Smtp Server.", 3 );
303                 append( sb, "Required: Yes", 3 );
304                 append( sb, "Expression: ${changes.smtpHost}", 3 );
305                 append( sb, "", 0 );
306 
307                 append( sb, "smtpPort (Default: 25)", 2 );
308                 append( sb, "Port.", 3 );
309                 append( sb, "Required: Yes", 3 );
310                 append( sb, "Expression: ${changes.smtpPort}", 3 );
311                 append( sb, "", 0 );
312 
313                 append( sb, "sslMode (Default: false)", 2 );
314                 append( sb, "If the email should be sent in SSL mode.", 3 );
315                 append( sb, "Expression: ${changes.sslMode}", 3 );
316                 append( sb, "", 0 );
317 
318                 append( sb, "subject (Default: [ANNOUNCEMENT] - ${project.name} ${project.version} released)", 2 );
319                 append( sb, "Subject for the email.", 3 );
320                 append( sb, "Required: Yes", 3 );
321                 append( sb, "Expression: ${changes.subject}", 3 );
322                 append( sb, "", 0 );
323 
324                 append( sb, "template (Default: announcement.vm)", 2 );
325                 append( sb, "The Velocity template used to format the announcement.", 3 );
326                 append( sb, "Required: Yes", 3 );
327                 append( sb, "Expression: ${changes.template}", 3 );
328                 append( sb, "", 0 );
329 
330                 append( sb, "templateOutputDirectory", 2 );
331                 append( sb, "Directory which contains the template for announcement email.", 3 );
332                 append( sb, "Required: Yes", 3 );
333                 append( sb, "Expression: ${project.build.directory}/announcement", 3 );
334                 append( sb, "", 0 );
335 
336                 append( sb, "toAddresses", 2 );
337                 append( sb, "Recipient email address.", 3 );
338                 append( sb, "Required: Yes", 3 );
339                 append( sb, "", 0 );
340 
341                 append( sb, "username", 2 );
342                 append( sb, "The username used to send the email.", 3 );
343                 append( sb, "Expression: ${changes.username}", 3 );
344                 append( sb, "", 0 );
345             }
346         }
347 
348         if ( goal == null || goal.length() <= 0 || "changes-check".equals( goal ) )
349         {
350             append( sb, "changes:changes-check", 0 );
351             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 );
352             append( sb, "", 0 );
353             if ( detail )
354             {
355                 append( sb, "Available parameters:", 1 );
356                 append( sb, "", 0 );
357 
358                 append( sb, "releaseDateFormat (Default: yyyy-MM-dd)", 2 );
359                 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 );
360                 append( sb, "Expression: ${changes.releaseDateFormat}", 3 );
361                 append( sb, "", 0 );
362 
363                 append( sb, "version (Default: ${project.version})", 2 );
364                 append( sb, "Version of the artifact.", 3 );
365                 append( sb, "Required: Yes", 3 );
366                 append( sb, "Expression: ${changes.version}", 3 );
367                 append( sb, "", 0 );
368 
369                 append( sb, "xmlPath (Default: src/changes/changes.xml)", 2 );
370                 append( sb, "The path of the changes.xml file that will be checked.", 3 );
371                 append( sb, "Expression: ${changes.xmlPath}", 3 );
372                 append( sb, "", 0 );
373             }
374         }
375 
376         if ( goal == null || goal.length() <= 0 || "changes-report".equals( goal ) )
377         {
378             append( sb, "changes:changes-report", 0 );
379             append( sb, "Goal which creates a nicely formatted Changes Report in html format from a changes.xml file.", 1 );
380             append( sb, "", 0 );
381             if ( detail )
382             {
383                 append( sb, "Available parameters:", 1 );
384                 append( sb, "", 0 );
385 
386                 append( sb, "addActionDate (Default: false)", 2 );
387                 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 );
388                 append( sb, "Expression: ${changes.addActionDate}", 3 );
389                 append( sb, "", 0 );
390 
391                 append( sb, "escapeHTML (Default: true)", 2 );
392                 append( sb, "Deprecated. using markup inside CDATA sections does not work for all output formats!", 3 );
393                 append( sb, "", 0 );
394                 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 );
395                 append( sb, "", 0 );
396 
397                 append( sb, "filteringChanges (Default: false)", 2 );
398                 append( sb, "applying filtering filtering \'a la\' resources plugin", 3 );
399                 append( sb, "", 0 );
400 
401                 append( sb, "issueLinkTemplate (Default: %URL%/ViewIssue.jspa?key=%ISSUE%)", 2 );
402                 append( sb, "Deprecated. As of 2.1 use issueLinkTemplatePerSystem : this one will be with system default", 3 );
403                 append( sb, "", 0 );
404                 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 );
405                 append( sb, "Expression: ${changes.issueLinkTemplate}", 3 );
406                 append( sb, "", 0 );
407 
408                 append( sb, "issueLinkTemplatePerSystem", 2 );
409                 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 );
410                 append( sb, "", 0 );
411 
412                 append( sb, "outputDirectory (Default: ${project.reporting.outputDirectory})", 2 );
413                 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 );
414                 append( sb, "", 0 );
415 
416                 append( sb, "outputEncoding (Default: ${project.reporting.outputEncoding})", 2 );
417                 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 );
418                 append( sb, "Expression: ${outputEncoding}", 3 );
419                 append( sb, "", 0 );
420 
421                 append( sb, "publishDateFormat (Default: yyyy-MM-dd)", 2 );
422                 append( sb, "Format to use for publishDate. The value will be available with the following expression ${publishDate}", 3 );
423                 append( sb, "", 0 );
424 
425                 append( sb, "publishDateLocale (Default: en)", 2 );
426                 append( sb, "Locale to use for publishDate when formatting", 3 );
427                 append( sb, "", 0 );
428 
429                 append( sb, "teamlist (Default: team-list.html)", 2 );
430                 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 );
431                 append( sb, "", 0 );
432 
433                 append( sb, "xmlPath (Default: src/changes/changes.xml)", 2 );
434                 append( sb, "The path of the changes.xml file that will be converted into an HTML report.", 3 );
435                 append( sb, "Expression: ${changes.xmlPath}", 3 );
436                 append( sb, "", 0 );
437             }
438         }
439 
440         if ( goal == null || goal.length() <= 0 || "changes-validate".equals( goal ) )
441         {
442             append( sb, "changes:changes-validate", 0 );
443             append( sb, "Goal which validate the changes.xml file.", 1 );
444             append( sb, "", 0 );
445             if ( detail )
446             {
447                 append( sb, "Available parameters:", 1 );
448                 append( sb, "", 0 );
449 
450                 append( sb, "changesXsdVersion (Default: 1.0.0)", 2 );
451                 append( sb, "The changes xsd version.", 3 );
452                 append( sb, "Expression: ${changes.xsdVersion}", 3 );
453                 append( sb, "", 0 );
454 
455                 append( sb, "failOnError (Default: false)", 2 );
456                 append( sb, "Mojo failure if validation failed. If not and validation failed only a warning will be logged.", 3 );
457                 append( sb, "Expression: ${changes.validate.failed}", 3 );
458                 append( sb, "", 0 );
459 
460                 append( sb, "xmlPath (Default: src/changes/changes.xml)", 2 );
461                 append( sb, "The path of the changes.xml file that will be converted into an HTML report.", 3 );
462                 append( sb, "Expression: ${changes.xmlPath}", 3 );
463                 append( sb, "", 0 );
464             }
465         }
466 
467         if ( goal == null || goal.length() <= 0 || "help".equals( goal ) )
468         {
469             append( sb, "changes:help", 0 );
470             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 );
471             append( sb, "", 0 );
472             if ( detail )
473             {
474                 append( sb, "Available parameters:", 1 );
475                 append( sb, "", 0 );
476 
477                 append( sb, "detail (Default: false)", 2 );
478                 append( sb, "If true, display all settable properties for each goal.", 3 );
479                 append( sb, "Expression: ${detail}", 3 );
480                 append( sb, "", 0 );
481 
482                 append( sb, "goal", 2 );
483                 append( sb, "The name of the goal for which to show help. If unspecified, all goals will be displayed.", 3 );
484                 append( sb, "Expression: ${goal}", 3 );
485                 append( sb, "", 0 );
486 
487                 append( sb, "indentSize (Default: 2)", 2 );
488                 append( sb, "The number of spaces per indentation level, should be positive.", 3 );
489                 append( sb, "Expression: ${indentSize}", 3 );
490                 append( sb, "", 0 );
491 
492                 append( sb, "lineLength (Default: 80)", 2 );
493                 append( sb, "The maximum length of a display line, should be positive.", 3 );
494                 append( sb, "Expression: ${lineLength}", 3 );
495                 append( sb, "", 0 );
496             }
497         }
498 
499         if ( goal == null || goal.length() <= 0 || "jira-report".equals( goal ) )
500         {
501             append( sb, "changes:jira-report", 0 );
502             append( sb, "Goal which downloads issues from the Issue Tracking System and generates a report.", 1 );
503             append( sb, "", 0 );
504             if ( detail )
505             {
506                 append( sb, "Available parameters:", 1 );
507                 append( sb, "", 0 );
508 
509                 append( sb, "columnNames (Default: Key,Summary,Status,Resolution,Assignee)", 2 );
510                 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 );
511                 append( sb, "", 0 );
512 
513                 append( sb, "component", 2 );
514                 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 );
515                 append( sb, "", 0 );
516 
517                 append( sb, "filter", 2 );
518                 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 );
519                 append( sb, "", 0 );
520 
521                 append( sb, "fixVersionIds", 2 );
522                 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 );
523                 append( sb, "", 0 );
524 
525                 append( sb, "jiraDatePattern (Default: EEE, d MMM yyyy HH:mm:ss Z)", 2 );
526                 append( sb, "The pattern used by dates in the JIRA XML-file. This is used to parse the Created and Updated fields.", 3 );
527                 append( sb, "", 0 );
528 
529                 append( sb, "jiraPassword", 2 );
530                 append( sb, "Defines the JIRA password for authentication into a private JIRA installation.", 3 );
531                 append( sb, "", 0 );
532 
533                 append( sb, "jiraUser", 2 );
534                 append( sb, "Defines the JIRA username for authentication into a private JIRA installation.", 3 );
535                 append( sb, "", 0 );
536 
537                 append( sb, "maxEntries (Default: 100)", 2 );
538                 append( sb, "Maximum number of entries to be fetched from JIRA.", 3 );
539                 append( sb, "", 0 );
540 
541                 append( sb, "onlyCurrentVersion (Default: false)", 2 );
542                 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 );
543                 append( sb, "", 0 );
544 
545                 append( sb, "outputDirectory (Default: ${project.reporting.outputDirectory})", 2 );
546                 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 );
547                 append( sb, "", 0 );
548 
549                 append( sb, "outputEncoding (Default: ${project.reporting.outputEncoding})", 2 );
550                 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 );
551                 append( sb, "Expression: ${outputEncoding}", 3 );
552                 append( sb, "", 0 );
553 
554                 append( sb, "priorityIds", 2 );
555                 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 );
556                 append( sb, "", 0 );
557 
558                 append( sb, "resolutionIds (Default: Fixed)", 2 );
559                 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 );
560                 append( sb, "", 0 );
561 
562                 append( sb, "sortColumnNames (Default: Priority DESC, Created DESC)", 2 );
563                 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 );
564                 append( sb, "", 0 );
565 
566                 append( sb, "statusIds (Default: Closed)", 2 );
567                 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.\nIf your installation of JIRA uses custom status IDs, you can reference them here by their numeric values. You can obtain them on the Statuses page (in 4.0.2 it\'s under Administration > Issue Settings > Statuses) - just hover over the Edit link for the status you want and you\'ll see something like <your JIRA URL>/secure/admin/EditStatus!default.jspa?id=12345; in this case the value is 12345.\n\nNote: In versions 2.0-beta-3 and earlier this parameter had no default value.\n", 3 );
568                 append( sb, "", 0 );
569 
570                 append( sb, "typeIds", 2 );
571                 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 );
572                 append( sb, "", 0 );
573 
574                 append( sb, "versionPrefix", 2 );
575                 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 );
576                 append( sb, "", 0 );
577 
578                 append( sb, "webPassword", 2 );
579                 append( sb, "Defines the http password for basic authentication into the JIRA webserver.", 3 );
580                 append( sb, "", 0 );
581 
582                 append( sb, "webUser", 2 );
583                 append( sb, "Defines the http user for basic authentication into the JIRA webserver.", 3 );
584                 append( sb, "", 0 );
585             }
586         }
587 
588         if ( goal == null || goal.length() <= 0 || "trac-report".equals( goal ) )
589         {
590             append( sb, "changes:trac-report", 0 );
591             append( sb, "Goal which downloads issues from the Issue Tracking System and generates a report.", 1 );
592             append( sb, "", 0 );
593             if ( detail )
594             {
595                 append( sb, "Available parameters:", 1 );
596                 append( sb, "", 0 );
597 
598                 append( sb, "columnNames (Default: Id,Type,Summary,Assignee,Reporter,Priority,Status,Resolution,Created,Updated)", 2 );
599                 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 );
600                 append( sb, "", 0 );
601 
602                 append( sb, "outputDirectory (Default: ${project.reporting.outputDirectory})", 2 );
603                 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 );
604                 append( sb, "", 0 );
605 
606                 append( sb, "outputEncoding (Default: ${project.reporting.outputEncoding})", 2 );
607                 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 );
608                 append( sb, "Expression: ${outputEncoding}", 3 );
609                 append( sb, "", 0 );
610 
611                 append( sb, "query (Default: order=id)", 2 );
612                 append( sb, "Defines the Trac query for searching ticket.", 3 );
613                 append( sb, "", 0 );
614 
615                 append( sb, "tracPassword", 2 );
616                 append( sb, "Defines the Trac password for authentication into a private Trac installation.", 3 );
617                 append( sb, "", 0 );
618 
619                 append( sb, "tracUser", 2 );
620                 append( sb, "Defines the Trac username for authentication into a private Trac installation.", 3 );
621                 append( sb, "", 0 );
622             }
623         }
624 
625         if ( getLog().isInfoEnabled() )
626         {
627             getLog().info( sb.toString() );
628         }
629     }
630 
631     /**
632      * <p>Repeat a String <code>n</code> times to form a new string.</p>
633      *
634      * @param str String to repeat
635      * @param repeat number of times to repeat str
636      * @return String with repeated String
637      * @throws NegativeArraySizeException if <code>repeat < 0</code>
638      * @throws NullPointerException if str is <code>null</code>
639      */
640     private static String repeat( String str, int repeat )
641     {
642         StringBuffer buffer = new StringBuffer( repeat * str.length() );
643 
644         for ( int i = 0; i < repeat; i++ )
645         {
646             buffer.append( str );
647         }
648 
649         return buffer.toString();
650     }
651 
652     /** 
653      * Append a description to the buffer by respecting the indentSize and lineLength parameters.
654      * <b>Note</b>: The last character is always a new line.
655      * 
656      * @param sb The buffer to append the description, not <code>null</code>.
657      * @param description The description, not <code>null</code>.
658      * @param indent The base indentation level of each line, must not be negative.
659      */
660     private void append( StringBuffer sb, String description, int indent )
661     {
662         for ( Iterator it = toLines( description, indent, indentSize, lineLength ).iterator(); it.hasNext(); )
663         {
664             sb.append( it.next().toString() ).append( '\n' );
665         }
666     }
667 
668     /** 
669      * Splits the specified text into lines of convenient display length.
670      * 
671      * @param text The text to split into lines, must not be <code>null</code>.
672      * @param indent The base indentation level of each line, must not be negative.
673      * @param indentSize The size of each indentation, must not be negative.
674      * @param lineLength The length of the line, must not be negative.
675      * @return The sequence of display lines, never <code>null</code>.
676      * @throws NegativeArraySizeException if <code>indent < 0</code>
677      */
678     private static List toLines( String text, int indent, int indentSize, int lineLength )
679     {
680         List lines = new ArrayList();
681 
682         String ind = repeat( "\t", indent );
683         String[] plainLines = text.split( "(\r\n)|(\r)|(\n)" );
684         for ( int i = 0; i < plainLines.length; i++ )
685         {
686             toLines( lines, ind + plainLines[i], indentSize, lineLength );
687         }
688 
689         return lines;
690     }
691 
692     /** 
693      * Adds the specified line to the output sequence, performing line wrapping if necessary.
694      * 
695      * @param lines The sequence of display lines, must not be <code>null</code>.
696      * @param line The line to add, must not be <code>null</code>.
697      * @param indentSize The size of each indentation, must not be negative.
698      * @param lineLength The length of the line, must not be negative.
699      */
700     private static void toLines( List lines, String line, int indentSize, int lineLength )
701     {
702         int lineIndent = getIndentLevel( line );
703         StringBuffer buf = new StringBuffer( 256 );
704         String[] tokens = line.split( " +" );
705         for ( int i = 0; i < tokens.length; i++ )
706         {
707             String token = tokens[i];
708             if ( i > 0 )
709             {
710                 if ( buf.length() + token.length() >= lineLength )
711                 {
712                     lines.add( buf.toString() );
713                     buf.setLength( 0 );
714                     buf.append( repeat( " ", lineIndent * indentSize ) );
715                 }
716                 else
717                 {
718                     buf.append( ' ' );
719                 }
720             }
721             for ( int j = 0; j < token.length(); j++ )
722             {
723                 char c = token.charAt( j );
724                 if ( c == '\t' )
725                 {
726                     buf.append( repeat( " ", indentSize - buf.length() % indentSize ) );
727                 }
728                 else if ( c == '\u00A0' )
729                 {
730                     buf.append( ' ' );
731                 }
732                 else
733                 {
734                     buf.append( c );
735                 }
736             }
737         }
738         lines.add( buf.toString() );
739     }
740 
741     /** 
742      * Gets the indentation level of the specified line.
743      * 
744      * @param line The line whose indentation level should be retrieved, must not be <code>null</code>.
745      * @return The indentation level of the line.
746      */
747     private static int getIndentLevel( String line )
748     {
749         int level = 0;
750         for ( int i = 0; i < line.length() && line.charAt( i ) == '\t'; i++ )
751         {
752             level++;
753         }
754         for ( int i = level + 1; i <= level + 4 && i < line.length(); i++ )
755         {
756             if ( line.charAt( i ) == '\t' )
757             {
758                 level++;
759                 break;
760             }
761         }
762         return level;
763     }
764 }