View Javadoc

1   package org.apache.maven.plugin.checkstyle;
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-checkstyle-plugin.<br/> Call <pre>  mvn checkstyle:help -Ddetail=true -Dgoal=&lt;goal-name&gt;</pre> to display parameter details.
12   *
13   * @version generated on Mon Aug 08 20:37:28 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-checkstyle-plugin:2.7", 0 );
69          append( sb, "", 0 );
70  
71          append( sb, "Maven Checkstyle Plugin", 0 );
72          append( sb, "Generates a report on violations of code style and optionally fails the build if violations are detected.", 1 );
73          append( sb, "", 0 );
74  
75          if ( goal == null || goal.length() <= 0 )
76          {
77              append( sb, "This plugin has 3 goals:", 0 );
78              append( sb, "", 0 );
79          }
80  
81          if ( goal == null || goal.length() <= 0 || "check".equals( goal ) )
82          {
83              append( sb, "checkstyle:check", 0 );
84              append( sb, "Perform a violation check against the last Checkstyle run to see if there are any violations. It reads the Checkstyle output file, counts the number of violations found and displays it on the console.", 1 );
85              append( sb, "", 0 );
86              if ( detail )
87              {
88                  append( sb, "Available parameters:", 1 );
89                  append( sb, "", 0 );
90  
91                  append( sb, "cacheFile (Default: ${project.build.directory}/checkstyle-cachefile)", 2 );
92                  append( sb, "Specifies the cache file used to speed up Checkstyle on successive runs.", 3 );
93                  append( sb, "", 0 );
94  
95                  append( sb, "configLocation (Default: config/sun_checks.xml)", 2 );
96                  append( sb, "Specifies the location of the XML configuration to use.\n\nPotential values are a filesystem path, a URL, or a classpath resource. This parameter expects that the contents of the location conform to the xml format (Checkstyle Checker module) configuration of rulesets.\n\nThis parameter is resolved as resource, URL, then file. If successfully resolved, the contents of the configuration is copied into the ${project.build.directory}/checkstyle-configuration.xml file before being passed to Checkstyle as a configuration.\n\nThere are 4 predefined rulesets.\n\n-\tconfig/sun_checks.xml: Sun Checks.\n-\tconfig/turbine_checks.xml: Turbine Checks.\n-\tconfig/avalon_checks.xml: Avalon Checks.\n-\tconfig/maven_checks.xml: Maven Source Checks.\n", 3 );
97                  append( sb, "Expression: ${checkstyle.config.location}", 3 );
98                  append( sb, "", 0 );
99  
100                 append( sb, "consoleOutput (Default: false)", 2 );
101                 append( sb, "Output errors to console.", 3 );
102                 append( sb, "", 0 );
103 
104                 append( sb, "encoding (Default: ${project.build.sourceEncoding})", 2 );
105                 append( sb, "The file encoding to use when reading the source files. If the property project.build.sourceEncoding is not set, the platform default encoding is used. Note: This parameter always overrides the property charset from Checkstyle\'s TreeWalker module.", 3 );
106                 append( sb, "Expression: ${encoding}", 3 );
107                 append( sb, "", 0 );
108 
109                 append( sb, "excludes", 2 );
110                 append( sb, "Specifies the names filter of the source files to be excluded for Checkstyle.", 3 );
111                 append( sb, "Expression: ${checkstyle.excludes}", 3 );
112                 append( sb, "", 0 );
113 
114                 append( sb, "failOnViolation (Default: true)", 2 );
115                 append( sb, "Do we fail the build on a violation?", 3 );
116                 append( sb, "Expression: ${checkstyle.failOnViolation}", 3 );
117                 append( sb, "", 0 );
118 
119                 append( sb, "failsOnError (Default: false)", 2 );
120                 append( sb, "Specifies if the build should fail upon a violation.", 3 );
121                 append( sb, "", 0 );
122 
123                 append( sb, "headerLocation (Default: LICENSE.txt)", 2 );
124                 append( sb, "Specifies the location of the License file (a.k.a. the header file) that can be used by Checkstyle to verify that source code has the correct license header.\n\nYou need to use ${checkstyle.header.file} in your Checkstyle xml configuration to reference the name of this header file.\n\nFor instance:\n\n<module name=\'RegexpHeader\'> <property name=\'headerFile\' value=\'${checkstyle.header.file}\'/> </module>\n", 3 );
125                 append( sb, "Expression: ${checkstyle.header.file}", 3 );
126                 append( sb, "", 0 );
127 
128                 append( sb, "includes (Default: **/*.java)", 2 );
129                 append( sb, "Specifies the names filter of the source files to be used for Checkstyle.", 3 );
130                 append( sb, "Required: Yes", 3 );
131                 append( sb, "Expression: ${checkstyle.includes}", 3 );
132                 append( sb, "", 0 );
133 
134                 append( sb, "includeTestSourceDirectory (Default: ${false})", 2 );
135                 append( sb, "Include or not the test source directory to be used for Checkstyle.", 3 );
136                 append( sb, "", 0 );
137 
138                 append( sb, "logViolationsToConsole (Default: false)", 2 );
139                 append( sb, "Output the detected violations to the console.", 3 );
140                 append( sb, "Expression: ${checkstyle.console}", 3 );
141                 append( sb, "", 0 );
142 
143                 append( sb, "maxAllowedViolations (Default: 0)", 2 );
144                 append( sb, "The maximum number of allowed violations. The execution fails only if the number of violations is above this limit.", 3 );
145                 append( sb, "Expression: ${checkstyle.maxAllowedViolations}", 3 );
146                 append( sb, "", 0 );
147 
148                 append( sb, "outputFile (Default: ${project.build.directory}/checkstyle-result.xml)", 2 );
149                 append( sb, "Specifies the path and filename to save the Checkstyle output. The format of the output file is determined by the outputFileFormat parameter.", 3 );
150                 append( sb, "Expression: ${checkstyle.output.file}", 3 );
151                 append( sb, "", 0 );
152 
153                 append( sb, "outputFileFormat (Default: xml)", 2 );
154                 append( sb, "Specifies the format of the output to be used when writing to the output file. Valid values are \'plain\' and \'xml\'.", 3 );
155                 append( sb, "Expression: ${checkstyle.output.format}", 3 );
156                 append( sb, "", 0 );
157 
158                 append( sb, "propertiesLocation", 2 );
159                 append( sb, "Specifies the location of the properties file.\n\nThis parameter is resolved as URL, File then resource. If successfully resolved, the contents of the properties location is copied into the ${project.build.directory}/checkstyle-checker.properties file before being passed to Checkstyle for loading.\n\nThe contents of the propertiesLocation will be made available to Checkstyle for specifying values for parameters within the xml configuration (specified in the configLocation parameter).\n", 3 );
160                 append( sb, "Expression: ${checkstyle.properties.location}", 3 );
161                 append( sb, "", 0 );
162 
163                 append( sb, "propertyExpansion", 2 );
164                 append( sb, "Allows for specifying raw property expansion information.", 3 );
165                 append( sb, "", 0 );
166 
167                 append( sb, "skip (Default: false)", 2 );
168                 append( sb, "Skip entire check.", 3 );
169                 append( sb, "Expression: ${checkstyle.skip}", 3 );
170                 append( sb, "", 0 );
171 
172                 append( sb, "skipExec (Default: false)", 2 );
173                 append( sb, "Skip checktyle execution will only scan the outputFile.", 3 );
174                 append( sb, "Expression: ${checkstyle.skipExec}", 3 );
175                 append( sb, "", 0 );
176 
177                 append( sb, "sourceDirectory (Default: ${project.build.sourceDirectory})", 2 );
178                 append( sb, "Specifies the location of the source directory to be used for Checkstyle.", 3 );
179                 append( sb, "Required: Yes", 3 );
180                 append( sb, "", 0 );
181 
182                 append( sb, "suppressionsFileExpression (Default: checkstyle.suppressions.file)", 2 );
183                 append( sb, "The key to be used in the properties for the suppressions file.", 3 );
184                 append( sb, "Expression: ${checkstyle.suppression.expression}", 3 );
185                 append( sb, "", 0 );
186 
187                 append( sb, "suppressionsLocation", 2 );
188                 append( sb, "Specifies the location of the suppressions XML file to use.\n\nThis parameter is resolved as resource, URL, then file. If successfully resolved, the contents of the suppressions XML is copied into the ${project.build.directory}/checkstyle-supressions.xml file before being passed to Checkstyle for loading.\n\nSee suppressionsFileExpression for the property that will be made available to your checkstyle configuration.\n", 3 );
189                 append( sb, "Expression: ${checkstyle.suppressions.location}", 3 );
190                 append( sb, "", 0 );
191 
192                 append( sb, "testSourceDirectory (Default: ${project.build.testSourceDirectory})", 2 );
193                 append( sb, "Specifies the location of the test source directory to be used for Checkstyle.", 3 );
194                 append( sb, "", 0 );
195 
196                 append( sb, "useFile", 2 );
197                 append( sb, "If null, the Checkstyle plugin will display violations on stdout. Otherwise, a text file will be created with the violations.", 3 );
198                 append( sb, "", 0 );
199 
200                 append( sb, "violationSeverity (Default: error)", 2 );
201                 append( sb, "The lowest severity level that is considered a violation. Valid values are \'error\', \'warning\' and \'info\'.", 3 );
202                 append( sb, "Expression: ${checkstyle.violationSeverity}", 3 );
203                 append( sb, "", 0 );
204             }
205         }
206 
207         if ( goal == null || goal.length() <= 0 || "checkstyle".equals( goal ) )
208         {
209             append( sb, "checkstyle:checkstyle", 0 );
210             append( sb, "Perform a Checkstyle analysis, and generate a report on violations.", 1 );
211             append( sb, "", 0 );
212             if ( detail )
213             {
214                 append( sb, "Available parameters:", 1 );
215                 append( sb, "", 0 );
216 
217                 append( sb, "cacheFile (Default: ${project.build.directory}/checkstyle-cachefile)", 2 );
218                 append( sb, "Specifies the cache file used to speed up Checkstyle on successive runs.", 3 );
219                 append( sb, "", 0 );
220 
221                 append( sb, "configLocation (Default: config/sun_checks.xml)", 2 );
222                 append( sb, "Specifies the location of the XML configuration to use.\n\nPotential values are a filesystem path, a URL, or a classpath resource. This parameter expects that the contents of the location conform to the xml format (Checkstyle Checker module) configuration of rulesets.\n\nThis parameter is resolved as resource, URL, then file. If successfully resolved, the contents of the configuration is copied into the ${project.build.directory}/checkstyle-configuration.xml file before being passed to Checkstyle as a configuration.\n\nThere are 4 predefined rulesets.\n\n-\tconfig/sun_checks.xml: Sun Checks.\n-\tconfig/turbine_checks.xml: Turbine Checks.\n-\tconfig/avalon_checks.xml: Avalon Checks.\n-\tconfig/maven_checks.xml: Maven Source Checks.\n", 3 );
223                 append( sb, "Expression: ${checkstyle.config.location}", 3 );
224                 append( sb, "", 0 );
225 
226                 append( sb, "consoleOutput (Default: false)", 2 );
227                 append( sb, "Output errors to console.", 3 );
228                 append( sb, "", 0 );
229 
230                 append( sb, "enableFilesSummary (Default: true)", 2 );
231                 append( sb, "Specifies if the Files summary should be enabled or not.", 3 );
232                 append( sb, "Expression: ${checkstyle.enable.files.summary}", 3 );
233                 append( sb, "", 0 );
234 
235                 append( sb, "enableRSS (Default: true)", 2 );
236                 append( sb, "Specifies if the RSS should be enabled or not.", 3 );
237                 append( sb, "Expression: ${checkstyle.enable.rss}", 3 );
238                 append( sb, "", 0 );
239 
240                 append( sb, "enableRulesSummary (Default: true)", 2 );
241                 append( sb, "Specifies if the Rules summary should be enabled or not.", 3 );
242                 append( sb, "Expression: ${checkstyle.enable.rules.summary}", 3 );
243                 append( sb, "", 0 );
244 
245                 append( sb, "enableSeveritySummary (Default: true)", 2 );
246                 append( sb, "Specifies if the Severity summary should be enabled or not.", 3 );
247                 append( sb, "Expression: ${checkstyle.enable.severity.summary}", 3 );
248                 append( sb, "", 0 );
249 
250                 append( sb, "encoding (Default: ${project.build.sourceEncoding})", 2 );
251                 append( sb, "The file encoding to use when reading the source files. If the property project.build.sourceEncoding is not set, the platform default encoding is used. Note: This parameter always overrides the property charset from Checkstyle\'s TreeWalker module.", 3 );
252                 append( sb, "Expression: ${encoding}", 3 );
253                 append( sb, "", 0 );
254 
255                 append( sb, "excludes", 2 );
256                 append( sb, "Specifies the names filter of the source files to be excluded for Checkstyle.", 3 );
257                 append( sb, "Expression: ${checkstyle.excludes}", 3 );
258                 append( sb, "", 0 );
259 
260                 append( sb, "failsOnError (Default: false)", 2 );
261                 append( sb, "Specifies if the build should fail upon a violation.", 3 );
262                 append( sb, "", 0 );
263 
264                 append( sb, "format (Default: sun)", 2 );
265                 append( sb, "Deprecated. Use configLocation instead.", 3 );
266                 append( sb, "", 0 );
267                 append( sb, "Specifies what predefined check set to use. Available sets are \'sun\' (for the Sun coding conventions), \'turbine\', and \'avalon\'.", 3 );
268                 append( sb, "", 0 );
269 
270                 append( sb, "headerFile", 2 );
271                 append( sb, "Deprecated. Use headerLocation instead.", 3 );
272                 append( sb, "", 0 );
273                 append( sb, "Specifies the location of the License file (a.k.a. the header file) that is used by Checkstyle to verify that source code has the correct license header.", 3 );
274                 append( sb, "Expression: ${basedir}/LICENSE.txt", 3 );
275                 append( sb, "", 0 );
276 
277                 append( sb, "headerLocation (Default: LICENSE.txt)", 2 );
278                 append( sb, "Specifies the location of the License file (a.k.a. the header file) that can be used by Checkstyle to verify that source code has the correct license header.\n\nYou need to use ${checkstyle.header.file} in your Checkstyle xml configuration to reference the name of this header file.\n\nFor instance:\n\n<module name=\'RegexpHeader\'> <property name=\'headerFile\' value=\'${checkstyle.header.file}\'/> </module>\n", 3 );
279                 append( sb, "Expression: ${checkstyle.header.file}", 3 );
280                 append( sb, "", 0 );
281 
282                 append( sb, "includes (Default: **/*.java)", 2 );
283                 append( sb, "Specifies the names filter of the source files to be used for Checkstyle.", 3 );
284                 append( sb, "Required: Yes", 3 );
285                 append( sb, "Expression: ${checkstyle.includes}", 3 );
286                 append( sb, "", 0 );
287 
288                 append( sb, "includeTestSourceDirectory (Default: ${false})", 2 );
289                 append( sb, "Include or not the test source directory to be used for Checkstyle.", 3 );
290                 append( sb, "", 0 );
291 
292                 append( sb, "linkXRef (Default: true)", 2 );
293                 append( sb, "Link the violation line numbers to the source xref. Will link automatically if Maven JXR plugin is being used.", 3 );
294                 append( sb, "Expression: ${linkXRef}", 3 );
295                 append( sb, "", 0 );
296 
297                 append( sb, "outputDirectory (Default: ${project.reporting.outputDirectory})", 2 );
298                 append( sb, "The output directory for the report. Note that this parameter is only evaluated if the goal is run directly from the command line. If the goal is run indirectly as part of a site generation, the output directory configured in Maven Site Plugin is used instead.", 3 );
299                 append( sb, "Required: Yes", 3 );
300                 append( sb, "", 0 );
301 
302                 append( sb, "outputFile (Default: ${project.build.directory}/checkstyle-result.xml)", 2 );
303                 append( sb, "Specifies the path and filename to save the checkstyle output. The format of the output file is determined by the outputFileFormat parameter.", 3 );
304                 append( sb, "Expression: ${checkstyle.output.file}", 3 );
305                 append( sb, "", 0 );
306 
307                 append( sb, "outputFileFormat (Default: xml)", 2 );
308                 append( sb, "Specifies the format of the output to be used when writing to the output file. Valid values are \'plain\' and \'xml\'.", 3 );
309                 append( sb, "Expression: ${checkstyle.output.format}", 3 );
310                 append( sb, "", 0 );
311 
312                 append( sb, "packageNamesFile", 2 );
313                 append( sb, "Deprecated. Use packageNamesLocation instead.", 3 );
314                 append( sb, "", 0 );
315                 append( sb, "Specifies the location of the package names XML to be used to configure Checkstyle.", 3 );
316                 append( sb, "", 0 );
317 
318                 append( sb, "packageNamesLocation", 2 );
319                 append( sb, "Specifies the location of the package names XML to be used to configure the Checkstyle Packages.\n\nThis parameter is resolved as resource, URL, then file. If resolved to a resource, or a URL, the contents of the package names XML is copied into the ${project.build.directory}/checkstyle-packagenames.xml file before being passed to Checkstyle for loading.\n", 3 );
320                 append( sb, "", 0 );
321 
322                 append( sb, "propertiesFile", 2 );
323                 append( sb, "Deprecated. Use propertiesLocation instead.", 3 );
324                 append( sb, "", 0 );
325                 append( sb, "Specifies the location of the Checkstyle properties file that will be used to check the source.", 3 );
326                 append( sb, "", 0 );
327 
328                 append( sb, "propertiesLocation", 2 );
329                 append( sb, "Specifies the location of the properties file.\n\nThis parameter is resolved as URL, File then resource. If successfully resolved, the contents of the properties location is copied into the ${project.build.directory}/checkstyle-checker.properties file before being passed to Checkstyle for loading.\n\nThe contents of the propertiesLocation will be made available to Checkstyle for specifying values for parameters within the xml configuration (specified in the configLocation parameter).\n", 3 );
330                 append( sb, "Expression: ${checkstyle.properties.location}", 3 );
331                 append( sb, "", 0 );
332 
333                 append( sb, "propertiesURL", 2 );
334                 append( sb, "Deprecated. Use propertiesLocation instead.", 3 );
335                 append( sb, "", 0 );
336                 append( sb, "Specifies the URL of the Checkstyle properties that will be used to check the source.", 3 );
337                 append( sb, "", 0 );
338 
339                 append( sb, "propertyExpansion", 2 );
340                 append( sb, "Allows for specifying raw property expansion information.", 3 );
341                 append( sb, "", 0 );
342 
343                 append( sb, "skip (Default: false)", 2 );
344                 append( sb, "Skip entire check.", 3 );
345                 append( sb, "Expression: ${checkstyle.skip}", 3 );
346                 append( sb, "", 0 );
347 
348                 append( sb, "sourceDirectory (Default: ${project.build.sourceDirectory})", 2 );
349                 append( sb, "Specifies the location of the source directory to be used for Checkstyle.", 3 );
350                 append( sb, "Required: Yes", 3 );
351                 append( sb, "", 0 );
352 
353                 append( sb, "suppressionsFile", 2 );
354                 append( sb, "Deprecated. Use suppressionsLocation instead.", 3 );
355                 append( sb, "", 0 );
356                 append( sb, "Specifies the location of the suppressions XML file to use. The plugin defines a Checkstyle property named checkstyle.suppressions.file with the value of this property. This allows using the Checkstyle property in your own custom checkstyle configuration file when specifying a suppressions file.", 3 );
357                 append( sb, "", 0 );
358 
359                 append( sb, "suppressionsFileExpression (Default: checkstyle.suppressions.file)", 2 );
360                 append( sb, "The key to be used in the properties for the suppressions file.", 3 );
361                 append( sb, "Expression: ${checkstyle.suppression.expression}", 3 );
362                 append( sb, "", 0 );
363 
364                 append( sb, "suppressionsLocation", 2 );
365                 append( sb, "Specifies the location of the suppressions XML file to use.\n\nThis parameter is resolved as resource, URL, then file. If successfully resolved, the contents of the suppressions XML is copied into the ${project.build.directory}/checkstyle-supressions.xml file before being passed to Checkstyle for loading.\n\nSee suppressionsFileExpression for the property that will be made available to your checkstyle configuration.\n", 3 );
366                 append( sb, "Expression: ${checkstyle.suppressions.location}", 3 );
367                 append( sb, "", 0 );
368 
369                 append( sb, "testSourceDirectory (Default: ${project.build.testSourceDirectory})", 2 );
370                 append( sb, "Specifies the location of the test source directory to be used for Checkstyle.", 3 );
371                 append( sb, "", 0 );
372 
373                 append( sb, "useFile", 2 );
374                 append( sb, "If null, the Checkstyle plugin will display violations on stdout. Otherwise, a text file will be created with the violations.", 3 );
375                 append( sb, "", 0 );
376 
377                 append( sb, "xrefLocation (Default: ${project.reporting.outputDirectory}/xref)", 2 );
378                 append( sb, "Location of the Xrefs to link to.", 3 );
379                 append( sb, "", 0 );
380             }
381         }
382 
383         if ( goal == null || goal.length() <= 0 || "help".equals( goal ) )
384         {
385             append( sb, "checkstyle:help", 0 );
386             append( sb, "Display help information on maven-checkstyle-plugin.\nCall\n\u00a0\u00a0mvn\u00a0checkstyle:help\u00a0-Ddetail=true\u00a0-Dgoal=<goal-name>\nto display parameter details.", 1 );
387             append( sb, "", 0 );
388             if ( detail )
389             {
390                 append( sb, "Available parameters:", 1 );
391                 append( sb, "", 0 );
392 
393                 append( sb, "detail (Default: false)", 2 );
394                 append( sb, "If true, display all settable properties for each goal.", 3 );
395                 append( sb, "Expression: ${detail}", 3 );
396                 append( sb, "", 0 );
397 
398                 append( sb, "goal", 2 );
399                 append( sb, "The name of the goal for which to show help. If unspecified, all goals will be displayed.", 3 );
400                 append( sb, "Expression: ${goal}", 3 );
401                 append( sb, "", 0 );
402 
403                 append( sb, "indentSize (Default: 2)", 2 );
404                 append( sb, "The number of spaces per indentation level, should be positive.", 3 );
405                 append( sb, "Expression: ${indentSize}", 3 );
406                 append( sb, "", 0 );
407 
408                 append( sb, "lineLength (Default: 80)", 2 );
409                 append( sb, "The maximum length of a display line, should be positive.", 3 );
410                 append( sb, "Expression: ${lineLength}", 3 );
411                 append( sb, "", 0 );
412             }
413         }
414 
415         if ( getLog().isInfoEnabled() )
416         {
417             getLog().info( sb.toString() );
418         }
419     }
420 
421     /**
422      * <p>Repeat a String <code>n</code> times to form a new string.</p>
423      *
424      * @param str String to repeat
425      * @param repeat number of times to repeat str
426      * @return String with repeated String
427      * @throws NegativeArraySizeException if <code>repeat < 0</code>
428      * @throws NullPointerException if str is <code>null</code>
429      */
430     private static String repeat( String str, int repeat )
431     {
432         StringBuffer buffer = new StringBuffer( repeat * str.length() );
433 
434         for ( int i = 0; i < repeat; i++ )
435         {
436             buffer.append( str );
437         }
438 
439         return buffer.toString();
440     }
441 
442     /** 
443      * Append a description to the buffer by respecting the indentSize and lineLength parameters.
444      * <b>Note</b>: The last character is always a new line.
445      * 
446      * @param sb The buffer to append the description, not <code>null</code>.
447      * @param description The description, not <code>null</code>.
448      * @param indent The base indentation level of each line, must not be negative.
449      */
450     private void append( StringBuffer sb, String description, int indent )
451     {
452         for ( Iterator it = toLines( description, indent, indentSize, lineLength ).iterator(); it.hasNext(); )
453         {
454             sb.append( it.next().toString() ).append( '\n' );
455         }
456     }
457 
458     /** 
459      * Splits the specified text into lines of convenient display length.
460      * 
461      * @param text The text to split into lines, must not be <code>null</code>.
462      * @param indent The base indentation level of each line, must not be negative.
463      * @param indentSize The size of each indentation, must not be negative.
464      * @param lineLength The length of the line, must not be negative.
465      * @return The sequence of display lines, never <code>null</code>.
466      * @throws NegativeArraySizeException if <code>indent < 0</code>
467      */
468     private static List toLines( String text, int indent, int indentSize, int lineLength )
469     {
470         List lines = new ArrayList();
471 
472         String ind = repeat( "\t", indent );
473         String[] plainLines = text.split( "(\r\n)|(\r)|(\n)" );
474         for ( int i = 0; i < plainLines.length; i++ )
475         {
476             toLines( lines, ind + plainLines[i], indentSize, lineLength );
477         }
478 
479         return lines;
480     }
481 
482     /** 
483      * Adds the specified line to the output sequence, performing line wrapping if necessary.
484      * 
485      * @param lines The sequence of display lines, must not be <code>null</code>.
486      * @param line The line to add, must not be <code>null</code>.
487      * @param indentSize The size of each indentation, must not be negative.
488      * @param lineLength The length of the line, must not be negative.
489      */
490     private static void toLines( List lines, String line, int indentSize, int lineLength )
491     {
492         int lineIndent = getIndentLevel( line );
493         StringBuffer buf = new StringBuffer( 256 );
494         String[] tokens = line.split( " +" );
495         for ( int i = 0; i < tokens.length; i++ )
496         {
497             String token = tokens[i];
498             if ( i > 0 )
499             {
500                 if ( buf.length() + token.length() >= lineLength )
501                 {
502                     lines.add( buf.toString() );
503                     buf.setLength( 0 );
504                     buf.append( repeat( " ", lineIndent * indentSize ) );
505                 }
506                 else
507                 {
508                     buf.append( ' ' );
509                 }
510             }
511             for ( int j = 0; j < token.length(); j++ )
512             {
513                 char c = token.charAt( j );
514                 if ( c == '\t' )
515                 {
516                     buf.append( repeat( " ", indentSize - buf.length() % indentSize ) );
517                 }
518                 else if ( c == '\u00A0' )
519                 {
520                     buf.append( ' ' );
521                 }
522                 else
523                 {
524                     buf.append( c );
525                 }
526             }
527         }
528         lines.add( buf.toString() );
529     }
530 
531     /** 
532      * Gets the indentation level of the specified line.
533      * 
534      * @param line The line whose indentation level should be retrieved, must not be <code>null</code>.
535      * @return The indentation level of the line.
536      */
537     private static int getIndentLevel( String line )
538     {
539         int level = 0;
540         for ( int i = 0; i < line.length() && line.charAt( i ) == '\t'; i++ )
541         {
542             level++;
543         }
544         for ( int i = level + 1; i <= level + 4 && i < line.length(); i++ )
545         {
546             if ( line.charAt( i ) == '\t' )
547             {
548                 level++;
549                 break;
550             }
551         }
552         return level;
553     }
554 }