View Javadoc

1   package org.apache.maven.archetype.mojos;
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-archetype-plugin.<br/> Call <pre>  mvn archetype:help -Ddetail=true -Dgoal=&lt;goal-name&gt;</pre> to display parameter details.
12   *
13   * @version generated on Sat Nov 19 08:05:28 EST 2011
14   * @author org.apache.maven.tools.plugin.generator.PluginHelpGenerator (version 2.9)
15   * @goal help
16   * @requiresProject false
17   * @threadSafe
18   */
19  @SuppressWarnings( "all" )
20  public class HelpMojo
21      extends AbstractMojo
22  {
23      /**
24       * If <code>true</code>, display all settable properties for each goal.
25       * 
26       * @parameter expression="${detail}" default-value="false"
27       */
28      private boolean detail;
29  
30      /**
31       * The name of the goal for which to show help. If unspecified, all goals will be displayed.
32       * 
33       * @parameter expression="${goal}"
34       */
35      private java.lang.String goal;
36  
37      /**
38       * The maximum length of a display line, should be positive.
39       * 
40       * @parameter expression="${lineLength}" default-value="80"
41       */
42      private int lineLength;
43  
44      /**
45       * The number of spaces per indentation level, should be positive.
46       * 
47       * @parameter expression="${indentSize}" default-value="2"
48       */
49      private int indentSize;
50  
51  
52      /** {@inheritDoc} */
53      public void execute()
54          throws MojoExecutionException
55      {
56          if ( lineLength <= 0 )
57          {
58              getLog().warn( "The parameter 'lineLength' should be positive, using '80' as default." );
59              lineLength = 80;
60          }
61          if ( indentSize <= 0 )
62          {
63              getLog().warn( "The parameter 'indentSize' should be positive, using '2' as default." );
64              indentSize = 2;
65          }
66  
67          StringBuffer sb = new StringBuffer();
68  
69          append( sb, "org.apache.maven.plugins:maven-archetype-plugin:2.2", 0 );
70          append( sb, "", 0 );
71  
72          append( sb, "Maven Archetype Plugin", 0 );
73          append( sb, "Maven Archetype is a set of tools to deal with archetypes, i.e. an abstract representation of a kind of project that can be instantiated into a concrete customized Maven project. An archetype knows which files will be part of the instantiated project and which properties to fill to properly customize the project.", 1 );
74          append( sb, "", 0 );
75  
76          if ( goal == null || goal.length() <= 0 )
77          {
78              append( sb, "This plugin has 8 goals:", 0 );
79              append( sb, "", 0 );
80          }
81  
82          if ( goal == null || goal.length() <= 0 || "crawl".equals( goal ) )
83          {
84              append( sb, "archetype:crawl", 0 );
85              append( sb, "Crawl a Maven repository (filesystem, not HTTP) and creates a catalog file.", 1 );
86              append( sb, "", 0 );
87              if ( detail )
88              {
89                  append( sb, "Available parameters:", 1 );
90                  append( sb, "", 0 );
91  
92                  append( sb, "catalogFile", 2 );
93                  append( sb, "The archetype\'s catalog to update.", 3 );
94                  append( sb, "Expression: ${catalog}", 3 );
95                  append( sb, "", 0 );
96  
97                  append( sb, "repository (Default: ${settings.localRepository})", 2 );
98                  append( sb, "The repository to crawl.", 3 );
99                  append( sb, "Expression: ${repository}", 3 );
100                 append( sb, "", 0 );
101             }
102         }
103 
104         if ( goal == null || goal.length() <= 0 || "create".equals( goal ) )
105         {
106             append( sb, "archetype:create", 0 );
107             append( sb, "Deprecated. Please use the generate mojo instead.", 1 );
108             if ( detail )
109             {
110                 append( sb, "", 0 );
111                 append( sb, "The archetype creation goal looks for an archetype with a given groupId, artifactId, and version and retrieves it from the remote repository. Once the archetype is retrieved, it is then processed against a set of user parameters to create a working Maven project.", 1 );
112             }
113             append( sb, "", 0 );
114             if ( detail )
115             {
116                 append( sb, "Available parameters:", 1 );
117                 append( sb, "", 0 );
118 
119                 append( sb, "archetypeArtifactId (Default: maven-archetype-quickstart)", 2 );
120                 append( sb, "The Archetype Artifact Id to be used.", 3 );
121                 append( sb, "Required: Yes", 3 );
122                 append( sb, "Expression: ${archetypeArtifactId}", 3 );
123                 append( sb, "", 0 );
124 
125                 append( sb, "archetypeGroupId (Default: org.apache.maven.archetypes)", 2 );
126                 append( sb, "The Archetype Group Id to be used.", 3 );
127                 append( sb, "Required: Yes", 3 );
128                 append( sb, "Expression: ${archetypeGroupId}", 3 );
129                 append( sb, "", 0 );
130 
131                 append( sb, "archetypeVersion (Default: RELEASE)", 2 );
132                 append( sb, "The Archetype Version to be used.", 3 );
133                 append( sb, "Required: Yes", 3 );
134                 append( sb, "Expression: ${archetypeVersion}", 3 );
135                 append( sb, "", 0 );
136 
137                 append( sb, "artifactId", 2 );
138                 append( sb, "The Artifact Id of the project to be build.", 3 );
139                 append( sb, "Expression: ${artifactId}", 3 );
140                 append( sb, "", 0 );
141 
142                 append( sb, "basedir (Default: ${user.dir})", 2 );
143                 append( sb, "(no description available)", 3 );
144                 append( sb, "Expression: ${basedir}", 3 );
145                 append( sb, "", 0 );
146 
147                 append( sb, "groupId", 2 );
148                 append( sb, "The Group Id of the project to be build.", 3 );
149                 append( sb, "Expression: ${groupId}", 3 );
150                 append( sb, "", 0 );
151 
152                 append( sb, "localRepository", 2 );
153                 append( sb, "Maven\'s local repository.", 3 );
154                 append( sb, "Required: Yes", 3 );
155                 append( sb, "Expression: ${localRepository}", 3 );
156                 append( sb, "", 0 );
157 
158                 append( sb, "packageName", 2 );
159                 append( sb, "The Package Name of the project to be build.", 3 );
160                 append( sb, "Expression: ${packageName}", 3 );
161                 append( sb, "", 0 );
162 
163                 append( sb, "pomRemoteRepositories", 2 );
164                 append( sb, "The remote repositories available for discovering dependencies and extensions as indicated by the POM.", 3 );
165                 append( sb, "Required: Yes", 3 );
166                 append( sb, "Expression: ${project.remoteArtifactRepositories}", 3 );
167                 append( sb, "", 0 );
168 
169                 append( sb, "project", 2 );
170                 append( sb, "The project to be created an archetype of.", 3 );
171                 append( sb, "Expression: ${project}", 3 );
172                 append( sb, "", 0 );
173 
174                 append( sb, "remoteRepositories", 2 );
175                 append( sb, "Other remote repositories available for discovering dependencies and extensions.", 3 );
176                 append( sb, "Expression: ${remoteRepositories}", 3 );
177                 append( sb, "", 0 );
178 
179                 append( sb, "version (Default: 1.0-SNAPSHOT)", 2 );
180                 append( sb, "The Version of the project to be build.", 3 );
181                 append( sb, "Required: Yes", 3 );
182                 append( sb, "Expression: ${version}", 3 );
183                 append( sb, "", 0 );
184             }
185         }
186 
187         if ( goal == null || goal.length() <= 0 || "create-from-project".equals( goal ) )
188         {
189             append( sb, "archetype:create-from-project", 0 );
190             append( sb, "Creates an archetype project from the current project.\n\nThis goal reads your source and resource files, the values of its parameters, and properties you specify in a .property file, and uses them to create a Maven archetype project using the maven-archetype packaging. If you build the resulting project, it will create the archetype. You can then use this archetype to create new projects that resemble the original.\n\nThe maven-archetype-plugin uses Velocity to expand template files, and this documentation talks about \'Velocity Properties\', which are values substituted into Velocity templates. See The Velocity User\'s Guide for more information.\n\nThis goal modifies the text of the files of the current project to form the Velocity template files that make up the archetype.\n\nGAV\n\tThe GAV values for the current project are replaced by properties: groupId, artifactId, and version. The user chooses new values for these when generating a project from the archetype.\npackage\n\tAll the files under one specified Java (or cognate) package are relocated to a project that the user chooses when generating a project. References to the class name are replaced by a property reference. For example, if the current project\'s sources are in the package org.apache.saltedpeanuts, then any example of the string org.apache.saltedpeanuts is replaced with the Velocity property reference ${packageName}. When the user generates a project, this is in turn replaced by his or her choice of a package.\ncustom properties\n\tYou may identify additional strings that should be replaced by parameters. To add custom properties, you must use the propertyFile parameter to specify a property file. See the documentation for propertyFile for the details.\n\nNote that you may need to edit the results of this goal. This goal has no way to exclude unwanted files, or add copyright notices to the Velocity templates, or add more complex elements to the archetype metadata file.\n\nThis goal also generates a simple integration-test that exercises the generated archetype.\n", 1 );
191             append( sb, "", 0 );
192             if ( detail )
193             {
194                 append( sb, "Available parameters:", 1 );
195                 append( sb, "", 0 );
196 
197                 append( sb, "archetypeFilteredExtentions", 2 );
198                 append( sb, "File extensions which are checked for project\'s text files (vs binary files).", 3 );
199                 append( sb, "Expression: ${archetype.filteredExtentions}", 3 );
200                 append( sb, "", 0 );
201 
202                 append( sb, "archetypeLanguages", 2 );
203                 append( sb, "Directory names which are checked for project\'s sources main package.", 3 );
204                 append( sb, "Expression: ${archetype.languages}", 3 );
205                 append( sb, "", 0 );
206 
207                 append( sb, "archetypePostPhase (Default: package)", 2 );
208                 append( sb, "The property telling which phase to call on the generated archetype. Interesting values are: package, integration-test, install and deploy.", 3 );
209                 append( sb, "Expression: ${archetype.postPhase}", 3 );
210                 append( sb, "", 0 );
211 
212                 append( sb, "archetypeRegistryFile", 2 );
213                 append( sb, "The location of the registry file.", 3 );
214                 append( sb, "Expression: ${user.home}/.m2/archetype.xml", 3 );
215                 append( sb, "", 0 );
216 
217                 append( sb, "defaultEncoding (Default: UTF-8)", 2 );
218                 append( sb, "Velocity templates encoding.", 3 );
219                 append( sb, "Expression: ${archetype.encoding}", 3 );
220                 append( sb, "", 0 );
221 
222                 append( sb, "interactive (Default: false)", 2 );
223                 append( sb, "Enable the interactive mode to define the archetype from the project.", 3 );
224                 append( sb, "Expression: ${interactive}", 3 );
225                 append( sb, "", 0 );
226 
227                 append( sb, "keepParent", 2 );
228                 append( sb, "POMs in archetype are created with their initial parent. This property is ignored when preserveCData is true.", 3 );
229                 append( sb, "Expression: ${archetype.keepParent}", 3 );
230                 append( sb, "", 0 );
231 
232                 append( sb, "outputDirectory", 2 );
233                 append( sb, "The directory where the archetype should be created.", 3 );
234                 append( sb, "Expression: ${project.build.directory}/generated-sources/archetype", 3 );
235                 append( sb, "", 0 );
236 
237                 append( sb, "packageName", 2 );
238                 append( sb, "The package name for Java source files to be incorporated in the archetype and and relocated to the package that the user selects.", 3 );
239                 append( sb, "Expression: ${packageName}", 3 );
240                 append( sb, "", 0 );
241 
242                 append( sb, "partialArchetype", 2 );
243                 append( sb, "Create a partial archetype.", 3 );
244                 append( sb, "Expression: ${archetype.partialArchetype}", 3 );
245                 append( sb, "", 0 );
246 
247                 append( sb, "preserveCData", 2 );
248                 append( sb, "Create pom\'s velocity templates with CDATA preservation. This uses the String.replaceAll() method and risks to have some overly replacement capabilities (beware of \'1.0\' value).", 3 );
249                 append( sb, "Expression: ${archetype.preserveCData}", 3 );
250                 append( sb, "", 0 );
251 
252                 append( sb, "propertyFile", 2 );
253                 append( sb, "The property file that holds the plugin configuration. If this is provided, then the plugin reads properties from here. The properties in here can be standard properties listed below or custom properties for this archetype. The standard properties are below. Several of them overlap parameters of this goal; it\'s better to just set the parameter.\npackage\n\tSee the packageName parameter.\narchetype.languages\n\tSee the archetypeLanguages parameter.\ngroupId\n\tThe default groupId of the generated project.\nartifactId\n\tThe default artifactId of the generated project.\nversion\n\tThe default version of the generated project.\narchetype.filteredExtensions\n\tSee the filteredExensions parameter.\nCustom Properties\nCustom properties allow you to replace some constant values in the project\'s files with Velocity macro references. When a user generates a project from your archetype he or she gets the opportunity to replace the value from the source project.\n\nCustom property names may not contain the \'.\' character.\n\nFor example, if you include a line like the following in your property file:\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0cxf-version=2.5.1-SNAPSHOT\nthe plugin will search your files for the string\n2.5.1-SNAPSHOT\nand replace them with references to a velocity macro\ncxf-version\n. It will then list\ncxf-version\nas a\nrequiredProperty\nin the archetype-metadata.xml, with\n2.5.1-SNAPSHOT\nas the default value.\n\n", 3 );
254                 append( sb, "Expression: ${archetype.properties}", 3 );
255                 append( sb, "", 0 );
256 
257                 append( sb, "testMode", 2 );
258                 append( sb, "(no description available)", 3 );
259                 append( sb, "Expression: ${testMode}", 3 );
260                 append( sb, "", 0 );
261             }
262         }
263 
264         if ( goal == null || goal.length() <= 0 || "generate".equals( goal ) )
265         {
266             append( sb, "archetype:generate", 0 );
267             append( sb, "Generates a new project from an archetype, or updated the actual project if using a partial archetype. If the project is fully generated, it is generated in a directory corresponding to its artifactId. If the project is updated with a partial archetype, it is done in the current directory.", 1 );
268             append( sb, "", 0 );
269             if ( detail )
270             {
271                 append( sb, "Available parameters:", 1 );
272                 append( sb, "", 0 );
273 
274                 append( sb, "archetypeArtifactId", 2 );
275                 append( sb, "The archetype\'s artifactId.", 3 );
276                 append( sb, "Expression: ${archetypeArtifactId}", 3 );
277                 append( sb, "", 0 );
278 
279                 append( sb, "archetypeCatalog (Default: remote,local)", 2 );
280                 append( sb, "The archetype catalogs to use to build a list and let the user choose from. It is a comma separated list of catalogs. Catalogs use following schemes:\n-\t\'file://...\' with archetype-catalog.xml automatically appended when pointing to a directory\n-\t\'http://...\' or \'https://...\' with archetype-catalog.xml always appended\n-\t\'local\' which is the shortcut for \'file://~/.m2/archetype-catalog.xml\'\n-\t\'remote\' which is the shortcut for Maven Central repository, ie \'http://repo1.maven.org/maven2\'\n-\t\'internal\' which is an internal catalog\nSince 2.0-alpha-5, default value is no longer internal,local but remote,local. If Maven Central repository catalog file is empty, internal catalog is used instead.", 3 );
281                 append( sb, "Expression: ${archetypeCatalog}", 3 );
282                 append( sb, "", 0 );
283 
284                 append( sb, "archetypeGroupId", 2 );
285                 append( sb, "The archetype\'s groupId.", 3 );
286                 append( sb, "Expression: ${archetypeGroupId}", 3 );
287                 append( sb, "", 0 );
288 
289                 append( sb, "archetypeRepository", 2 );
290                 append( sb, "The archetype\'s repository.", 3 );
291                 append( sb, "Expression: ${archetypeRepository}", 3 );
292                 append( sb, "", 0 );
293 
294                 append( sb, "archetypeVersion", 2 );
295                 append( sb, "The archetype\'s version.", 3 );
296                 append( sb, "Expression: ${archetypeVersion}", 3 );
297                 append( sb, "", 0 );
298 
299                 append( sb, "basedir", 2 );
300                 append( sb, "(no description available)", 3 );
301                 append( sb, "Expression: ${basedir}", 3 );
302                 append( sb, "", 0 );
303 
304                 append( sb, "filter", 2 );
305                 append( sb, "Applying some filter on displayed archetypes list: format is artifactId or groupId:artifactId.\n-\torg.apache: -> displays all archetypes which contain org.apache in groupId\n-\t:jee or jee -> displays all archetypes which contain jee in artifactId\n-\torg.apache:jee -> displays all archetypes which contain org.apache in groupId AND jee in artifactId\n", 3 );
306                 append( sb, "Expression: ${filter}", 3 );
307                 append( sb, "", 0 );
308 
309                 append( sb, "goals", 2 );
310                 append( sb, "Additional goals to immediately run on the project created from the archetype.", 3 );
311                 append( sb, "Expression: ${goals}", 3 );
312                 append( sb, "", 0 );
313 
314                 append( sb, "interactiveMode (Default: ${settings.interactiveMode})", 2 );
315                 append( sb, "User settings use to check the interactiveMode.", 3 );
316                 append( sb, "Required: Yes", 3 );
317                 append( sb, "Expression: ${interactiveMode}", 3 );
318                 append( sb, "", 0 );
319             }
320         }
321 
322         if ( goal == null || goal.length() <= 0 || "help".equals( goal ) )
323         {
324             append( sb, "archetype:help", 0 );
325             append( sb, "Display help information on maven-archetype-plugin.\nCall\n\u00a0\u00a0mvn\u00a0archetype:help\u00a0-Ddetail=true\u00a0-Dgoal=<goal-name>\nto display parameter details.", 1 );
326             append( sb, "", 0 );
327             if ( detail )
328             {
329                 append( sb, "Available parameters:", 1 );
330                 append( sb, "", 0 );
331 
332                 append( sb, "detail (Default: false)", 2 );
333                 append( sb, "If true, display all settable properties for each goal.", 3 );
334                 append( sb, "Expression: ${detail}", 3 );
335                 append( sb, "", 0 );
336 
337                 append( sb, "goal", 2 );
338                 append( sb, "The name of the goal for which to show help. If unspecified, all goals will be displayed.", 3 );
339                 append( sb, "Expression: ${goal}", 3 );
340                 append( sb, "", 0 );
341 
342                 append( sb, "indentSize (Default: 2)", 2 );
343                 append( sb, "The number of spaces per indentation level, should be positive.", 3 );
344                 append( sb, "Expression: ${indentSize}", 3 );
345                 append( sb, "", 0 );
346 
347                 append( sb, "lineLength (Default: 80)", 2 );
348                 append( sb, "The maximum length of a display line, should be positive.", 3 );
349                 append( sb, "Expression: ${lineLength}", 3 );
350                 append( sb, "", 0 );
351             }
352         }
353 
354         if ( goal == null || goal.length() <= 0 || "integration-test".equals( goal ) )
355         {
356             append( sb, "archetype:integration-test", 0 );
357             append( sb, "Execute the archetype integration tests, consisting in generating projects from the current archetype and optionally comparing generated projects with reference copy.\n\nEach IT consists of a sub-directory in src/test/resources/projects containing:\n\n-\ta goal.txt file, containing a list of goals to run against the generated project (can be empty, content ignored before maven-archetype-plugin 2.1),\n-\tan archetype.properties file, containing properties for project generation,\n-\tan optional reference/ directory containing a reference copy of the expected project created from the IT.\nNotice that it is expected to be run as part as of a build after the package phase and not directly as a goal from CLI.", 1 );
358             append( sb, "", 0 );
359             if ( detail )
360             {
361                 append( sb, "Available parameters:", 1 );
362                 append( sb, "", 0 );
363 
364                 append( sb, "debug (Default: false)", 2 );
365                 append( sb, "Whether to show debug statements in the build output.", 3 );
366                 append( sb, "Expression: ${archetype.test.debug}", 3 );
367                 append( sb, "", 0 );
368 
369                 append( sb, "encoding (Default: ${project.build.sourceEncoding})", 2 );
370                 append( sb, "The file encoding for the post-build script.", 3 );
371                 append( sb, "Expression: ${encoding}", 3 );
372                 append( sb, "", 0 );
373 
374                 append( sb, "filterProperties", 2 );
375                 append( sb, "A list of additional properties which will be used to filter tokens in settings.xml", 3 );
376                 append( sb, "", 0 );
377 
378                 append( sb, "noLog (Default: false)", 2 );
379                 append( sb, "Suppress logging to the build.log file.", 3 );
380                 append( sb, "Expression: ${archetype.test.noLog}", 3 );
381                 append( sb, "", 0 );
382 
383                 append( sb, "postBuildHookScript (Default: verify)", 2 );
384                 append( sb, "Relative path of a cleanup/verification hook script to run after executing the build. This script may be written with either BeanShell or Groovy. If the file extension is omitted (e.g. verify), the plugin searches for the file by trying out the well-known extensions .bsh and .groovy. If this script exists for a particular project but returns any non-null value different from true or throws an exception, the corresponding build is flagged as a failure.", 3 );
385                 append( sb, "Expression: ${archetype.test.verifyScript}", 3 );
386                 append( sb, "", 0 );
387 
388                 append( sb, "settingsFile", 2 );
389                 append( sb, "Path to an alternate settings.xml to use for Maven invocation with all ITs. Note that the <localRepository> element of this settings file is always ignored, i.e. the path given by the parameter localRepositoryPath is dominant.", 3 );
390                 append( sb, "Expression: ${archetype.test.settingsFile}", 3 );
391                 append( sb, "", 0 );
392 
393                 append( sb, "showVersion (Default: false)", 2 );
394                 append( sb, "flag to enable show mvn version used for running its (cli option : -V,--show-version )", 3 );
395                 append( sb, "Expression: ${archetype.test.showVersion}", 3 );
396                 append( sb, "", 0 );
397 
398                 append( sb, "streamLogs (Default: true)", 2 );
399                 append( sb, "Flag used to determine whether the build logs should be output to the normal mojo log.", 3 );
400                 append( sb, "Expression: ${archetype.test.streamLogs}", 3 );
401                 append( sb, "", 0 );
402 
403                 append( sb, "testProjectsDirectory (Default: ${project.build.testOutputDirectory}/projects)", 2 );
404                 append( sb, "Directory of test projects", 3 );
405                 append( sb, "Required: Yes", 3 );
406                 append( sb, "Expression: ${archetype.test.projectsDirectory}", 3 );
407                 append( sb, "", 0 );
408             }
409         }
410 
411         if ( goal == null || goal.length() <= 0 || "jar".equals( goal ) )
412         {
413             append( sb, "archetype:jar", 0 );
414             append( sb, "Build a JAR from the current Archetype project.", 1 );
415             append( sb, "", 0 );
416             if ( detail )
417             {
418                 append( sb, "Available parameters:", 1 );
419                 append( sb, "", 0 );
420 
421                 append( sb, "archetypeDirectory", 2 );
422                 append( sb, "Directory containing the classes.", 3 );
423                 append( sb, "Required: Yes", 3 );
424                 append( sb, "Expression: ${project.build.outputDirectory}", 3 );
425                 append( sb, "", 0 );
426 
427                 append( sb, "finalName", 2 );
428                 append( sb, "Name of the generated JAR.", 3 );
429                 append( sb, "Required: Yes", 3 );
430                 append( sb, "Expression: ${project.build.finalName}", 3 );
431                 append( sb, "", 0 );
432 
433                 append( sb, "outputDirectory", 2 );
434                 append( sb, "Directory containing the generated JAR.", 3 );
435                 append( sb, "Required: Yes", 3 );
436                 append( sb, "Expression: ${project.build.directory}", 3 );
437                 append( sb, "", 0 );
438             }
439         }
440 
441         if ( goal == null || goal.length() <= 0 || "update-local-catalog".equals( goal ) )
442         {
443             append( sb, "archetype:update-local-catalog", 0 );
444             append( sb, "Updates the local catalog", 1 );
445             append( sb, "", 0 );
446             if ( detail )
447             {
448                 append( sb, "Available parameters:", 1 );
449                 append( sb, "", 0 );
450             }
451         }
452 
453         if ( getLog().isInfoEnabled() )
454         {
455             getLog().info( sb.toString() );
456         }
457     }
458 
459     /**
460      * <p>Repeat a String <code>n</code> times to form a new string.</p>
461      *
462      * @param str String to repeat
463      * @param repeat number of times to repeat str
464      * @return String with repeated String
465      * @throws NegativeArraySizeException if <code>repeat < 0</code>
466      * @throws NullPointerException if str is <code>null</code>
467      */
468     private static String repeat( String str, int repeat )
469     {
470         StringBuffer buffer = new StringBuffer( repeat * str.length() );
471 
472         for ( int i = 0; i < repeat; i++ )
473         {
474             buffer.append( str );
475         }
476 
477         return buffer.toString();
478     }
479 
480     /** 
481      * Append a description to the buffer by respecting the indentSize and lineLength parameters.
482      * <b>Note</b>: The last character is always a new line.
483      * 
484      * @param sb The buffer to append the description, not <code>null</code>.
485      * @param description The description, not <code>null</code>.
486      * @param indent The base indentation level of each line, must not be negative.
487      */
488     private void append( StringBuffer sb, String description, int indent )
489     {
490         for ( Iterator it = toLines( description, indent, indentSize, lineLength ).iterator(); it.hasNext(); )
491         {
492             sb.append( it.next().toString() ).append( '\n' );
493         }
494     }
495 
496     /** 
497      * Splits the specified text into lines of convenient display length.
498      * 
499      * @param text The text to split into lines, must not be <code>null</code>.
500      * @param indent The base indentation level of each line, must not be negative.
501      * @param indentSize The size of each indentation, must not be negative.
502      * @param lineLength The length of the line, must not be negative.
503      * @return The sequence of display lines, never <code>null</code>.
504      * @throws NegativeArraySizeException if <code>indent < 0</code>
505      */
506     private static List toLines( String text, int indent, int indentSize, int lineLength )
507     {
508         List<String> lines = new ArrayList<String>();
509 
510         String ind = repeat( "\t", indent );
511         String[] plainLines = text.split( "(\r\n)|(\r)|(\n)" );
512         for ( int i = 0; i < plainLines.length; i++ )
513         {
514             toLines( lines, ind + plainLines[i], indentSize, lineLength );
515         }
516 
517         return lines;
518     }
519 
520     /** 
521      * Adds the specified line to the output sequence, performing line wrapping if necessary.
522      * 
523      * @param lines The sequence of display lines, must not be <code>null</code>.
524      * @param line The line to add, must not be <code>null</code>.
525      * @param indentSize The size of each indentation, must not be negative.
526      * @param lineLength The length of the line, must not be negative.
527      */
528     private static void toLines( List<String> lines, String line, int indentSize, int lineLength )
529     {
530         int lineIndent = getIndentLevel( line );
531         StringBuffer buf = new StringBuffer( 256 );
532         String[] tokens = line.split( " +" );
533         for ( int i = 0; i < tokens.length; i++ )
534         {
535             String token = tokens[i];
536             if ( i > 0 )
537             {
538                 if ( buf.length() + token.length() >= lineLength )
539                 {
540                     lines.add( buf.toString() );
541                     buf.setLength( 0 );
542                     buf.append( repeat( " ", lineIndent * indentSize ) );
543                 }
544                 else
545                 {
546                     buf.append( ' ' );
547                 }
548             }
549             for ( int j = 0; j < token.length(); j++ )
550             {
551                 char c = token.charAt( j );
552                 if ( c == '\t' )
553                 {
554                     buf.append( repeat( " ", indentSize - buf.length() % indentSize ) );
555                 }
556                 else if ( c == '\u00A0' )
557                 {
558                     buf.append( ' ' );
559                 }
560                 else
561                 {
562                     buf.append( c );
563                 }
564             }
565         }
566         lines.add( buf.toString() );
567     }
568 
569     /** 
570      * Gets the indentation level of the specified line.
571      * 
572      * @param line The line whose indentation level should be retrieved, must not be <code>null</code>.
573      * @return The indentation level of the line.
574      */
575     private static int getIndentLevel( String line )
576     {
577         int level = 0;
578         for ( int i = 0; i < line.length() && line.charAt( i ) == '\t'; i++ )
579         {
580             level++;
581         }
582         for ( int i = level + 1; i <= level + 4 && i < line.length(); i++ )
583         {
584             if ( line.charAt( i ) == '\t' )
585             {
586                 level++;
587                 break;
588             }
589         }
590         return level;
591     }
592 }