View Javadoc

1   
2   import org.apache.maven.plugin.AbstractMojo;
3   import org.apache.maven.plugin.MojoExecutionException;
4   import org.codehaus.plexus.util.ReaderFactory;
5   import org.codehaus.plexus.util.StringUtils;
6   import org.codehaus.plexus.util.xml.Xpp3Dom;
7   import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
8   import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
9   
10  import java.io.IOException;
11  import java.io.InputStream;
12  import java.util.ArrayList;
13  import java.util.Iterator;
14  import java.util.List;
15  
16  /**
17   * Display help information on maven-pdf-plugin.<br/>
18   * Call <code>mvn pdf:help -Ddetail=true -Dgoal=&lt;goal-name&gt;</code> to display parameter details.
19   * @author
20   * @version
21   * @goal help
22   * @requiresProject false
23   * @threadSafe
24   */
25  public class HelpMojo
26      extends AbstractMojo
27  {
28      /**
29       * If <code>true</code>, display all settable properties for each goal.
30       *
31       * @parameter property="detail" default-value="false"
32       */
33      //@Parameter( property = "detail", defaultValue = "false" )
34      private boolean detail;
35  
36      /**
37       * The name of the goal for which to show help. If unspecified, all goals will be displayed.
38       *
39       * @parameter property="goal"
40       */
41      //@Parameter( property = "goal" )
42      private java.lang.String goal;
43  
44      /**
45       * The maximum length of a display line, should be positive.
46       *
47       * @parameter property="lineLength" default-value="80"
48       */
49      //@Parameter( property = "lineLength", defaultValue = "80" )
50      private int lineLength;
51  
52      /**
53       * The number of spaces per indentation level, should be positive.
54       *
55       * @parameter property="indentSize" default-value="2"
56       */
57      //@Parameter( property = "indentSize", defaultValue = "2" )
58      private int indentSize;
59  
60      // groupId/artifactId/plugin-help.xml
61      private static final String PLUGIN_HELP_PATH = "/META-INF/maven/org.apache.maven.plugins/maven-pdf-plugin/plugin-help.xml";
62  
63      private Xpp3Dom build()
64          throws MojoExecutionException
65      {
66          getLog().debug( "load plugin-help.xml: " + PLUGIN_HELP_PATH );
67          InputStream is = getClass().getResourceAsStream( PLUGIN_HELP_PATH );
68          try
69          {
70              return Xpp3DomBuilder.build( ReaderFactory.newXmlReader( is ) );
71          }
72          catch ( XmlPullParserException e )
73          {
74              throw new MojoExecutionException( e.getMessage(), e );
75          }
76          catch ( IOException e )
77          {
78              throw new MojoExecutionException( e.getMessage(), e );
79          }
80      }
81  
82      /**
83       * {@inheritDoc}
84       */
85      public void execute()
86          throws MojoExecutionException
87      {
88          if ( lineLength <= 0 )
89          {
90              getLog().warn( "The parameter 'lineLength' should be positive, using '80' as default." );
91              lineLength = 80;
92          }
93          if ( indentSize <= 0 )
94          {
95              getLog().warn( "The parameter 'indentSize' should be positive, using '2' as default." );
96              indentSize = 2;
97          }
98  
99          Xpp3Dom pluginElement = build();
100 
101         StringBuilder sb = new StringBuilder();
102         String name = pluginElement.getChild( "name" ).getValue();
103         String version = pluginElement.getChild( "version" ).getValue();
104         String id = pluginElement.getChild( "groupId" ).getValue() + ":" + pluginElement.getChild( "artifactId" ).getValue()
105                     + ":" + version;
106         if ( StringUtils.isNotEmpty( name ) && !name.contains( id ) )
107         {
108             append( sb, name + " " + version, 0 );
109         }
110         else
111         {
112             if ( StringUtils.isNotEmpty( name ) )
113             {
114                 append( sb, name, 0 );
115             }
116             else
117             {
118                 append( sb, id, 0 );
119             }
120         }
121         append( sb, pluginElement.getChild( "description" ).getValue(), 1 );
122         append( sb, "", 0 );
123 
124         //<goalPrefix>plugin</goalPrefix>
125         String goalPrefix = pluginElement.getChild( "goalPrefix" ).getValue();
126 
127         Xpp3Dom[] mojos = pluginElement.getChild( "mojos" ).getChildren( "mojo" );
128 
129         if ( goal == null || goal.length() <= 0 )
130         {
131             append( sb, "This plugin has " + mojos.length + ( mojos.length > 1 ? " goals:" : " goal:" ) , 0 );
132             append( sb, "", 0 );
133         }
134 
135         for ( Xpp3Dom mojo : mojos )
136         {
137             writeGoal( sb, goalPrefix, mojo );
138         }
139 
140         if ( getLog().isInfoEnabled() )
141         {
142             getLog().info( sb.toString() );
143         }
144     }
145 
146     private String getValue( Xpp3Dom mojo, String child )
147     {
148         Xpp3Dom elt = mojo.getChild( child );
149         return ( elt == null ) ? "" : elt.getValue();
150     }
151 
152     private void writeGoal( StringBuilder sb, String goalPrefix, Xpp3Dom mojo )
153     {
154         String mojoGoal = mojo.getChild( "goal" ).getValue();
155         Xpp3Dom configurationElement = mojo.getChild( "configuration" );
156 
157         if ( goal == null || goal.length() <= 0 || mojoGoal.equals( goal ) )
158         {
159             append( sb, goalPrefix + ":" + mojoGoal, 0 );
160             Xpp3Dom deprecated = mojo.getChild( "deprecated" );
161             if ( ( deprecated != null ) && StringUtils.isNotEmpty( deprecated.getValue() ) )
162             {
163                 append( sb, "Deprecated. " + deprecated, 1 );
164                 if ( detail )
165                 {
166                     append( sb, "", 0 );
167                     append( sb, getValue( mojo, "description" ), 1 );
168                 }
169             }
170             else
171             {
172                 append( sb, getValue( mojo, "description" ), 1 );
173             }
174             append( sb, "", 0 );
175 
176             if ( detail )
177             {
178                 Xpp3Dom[] parameters = mojo.getChild( "parameters" ).getChildren( "parameter" );
179                 append( sb, "Available parameters:", 1 );
180                 append( sb, "", 0 );
181 
182                 for ( Xpp3Dom parameter : parameters )
183                 {
184                     writeParameter( sb, parameter, configurationElement );
185                 }
186             }
187         }
188     }
189 
190     private void writeParameter( StringBuilder sb, Xpp3Dom parameter, Xpp3Dom configurationElement )
191     {
192         String parameterName = parameter.getChild( "name" ).getValue();
193         String parameterDescription = parameter.getChild( "description" ).getValue();
194 
195         Xpp3Dom fieldConfigurationElement = configurationElement.getChild( parameterName );
196 
197         String parameterDefaultValue = "";
198         if ( fieldConfigurationElement != null && fieldConfigurationElement.getValue() != null )
199         {
200             parameterDefaultValue = " (Default: " + fieldConfigurationElement.getAttribute( "default-value" ) + ")";
201         }
202         append( sb, parameterName + parameterDefaultValue, 2 );
203         Xpp3Dom deprecated = parameter.getChild( "deprecated" );
204         if ( ( deprecated != null ) && StringUtils.isNotEmpty( deprecated.getValue() ) )
205         {
206             append( sb, "Deprecated. " + deprecated.getValue(), 3 );
207             append( sb, "", 0 );
208         }
209         append( sb, parameterDescription, 3 );
210         if ( "true".equals( parameter.getChild( "required" ).getValue() ) )
211         {
212             append( sb, "Required: Yes", 3 );
213         }
214         Xpp3Dom expression = parameter.getChild( "expression" );
215         if ( ( expression != null ) && StringUtils.isNotEmpty( expression.getValue() ) )
216         {
217             append( sb, "Expression: " + expression.getValue(), 3 );
218         }
219 
220         append( sb, "", 0 );
221     }
222 
223     /**
224      * <p>Repeat a String <code>n</code> times to form a new string.</p>
225      *
226      * @param str    String to repeat
227      * @param repeat number of times to repeat str
228      * @return String with repeated String
229      * @throws NegativeArraySizeException if <code>repeat < 0</code>
230      * @throws NullPointerException       if str is <code>null</code>
231      */
232     private static String repeat( String str, int repeat )
233     {
234         StringBuilder buffer = new StringBuilder( repeat * str.length() );
235 
236         for ( int i = 0; i < repeat; i++ )
237         {
238             buffer.append( str );
239         }
240 
241         return buffer.toString();
242     }
243 
244     /**
245      * Append a description to the buffer by respecting the indentSize and lineLength parameters.
246      * <b>Note</b>: The last character is always a new line.
247      *
248      * @param sb          The buffer to append the description, not <code>null</code>.
249      * @param description The description, not <code>null</code>.
250      * @param indent      The base indentation level of each line, must not be negative.
251      */
252     private void append( StringBuilder sb, String description, int indent )
253     {
254         for ( String line : toLines( description, indent, indentSize, lineLength ) )
255         {
256             sb.append( line ).append( '\n' );
257         }
258     }
259 
260     /**
261      * Splits the specified text into lines of convenient display length.
262      *
263      * @param text       The text to split into lines, must not be <code>null</code>.
264      * @param indent     The base indentation level of each line, must not be negative.
265      * @param indentSize The size of each indentation, must not be negative.
266      * @param lineLength The length of the line, must not be negative.
267      * @return The sequence of display lines, never <code>null</code>.
268      * @throws NegativeArraySizeException if <code>indent < 0</code>
269      */
270     private static List<String> toLines( String text, int indent, int indentSize, int lineLength )
271     {
272         List<String> lines = new ArrayList<String>();
273 
274         String ind = repeat( "\t", indent );
275 
276         String[] plainLines = text.split( "(\r\n)|(\r)|(\n)" );
277 
278         for ( String plainLine : plainLines )
279         {
280             toLines( lines, ind + plainLine, indentSize, lineLength );
281         }
282 
283         return lines;
284     }
285 
286     /**
287      * Adds the specified line to the output sequence, performing line wrapping if necessary.
288      *
289      * @param lines      The sequence of display lines, must not be <code>null</code>.
290      * @param line       The line to add, must not be <code>null</code>.
291      * @param indentSize The size of each indentation, must not be negative.
292      * @param lineLength The length of the line, must not be negative.
293      */
294     private static void toLines( List<String> lines, String line, int indentSize, int lineLength )
295     {
296         int lineIndent = getIndentLevel( line );
297         StringBuilder buf = new StringBuilder( 256 );
298 
299         String[] tokens = line.split( " +" );
300 
301         for ( String token : tokens )
302         {
303             if ( buf.length() > 0 )
304             {
305                 if ( buf.length() + token.length() >= lineLength )
306                 {
307                     lines.add( buf.toString() );
308                     buf.setLength( 0 );
309                     buf.append( repeat( " ", lineIndent * indentSize ) );
310                 }
311                 else
312                 {
313                     buf.append( ' ' );
314                 }
315             }
316 
317             for ( int j = 0; j < token.length(); j++ )
318             {
319                 char c = token.charAt( j );
320                 if ( c == '\t' )
321                 {
322                     buf.append( repeat( " ", indentSize - buf.length() % indentSize ) );
323                 }
324                 else if ( c == '\u00A0' )
325                 {
326                     buf.append( ' ' );
327                 }
328                 else
329                 {
330                     buf.append( c );
331                 }
332             }
333         }
334         lines.add( buf.toString() );
335     }
336 
337     /**
338      * Gets the indentation level of the specified line.
339      *
340      * @param line The line whose indentation level should be retrieved, must not be <code>null</code>.
341      * @return The indentation level of the line.
342      */
343     private static int getIndentLevel( String line )
344     {
345         int level = 0;
346         for ( int i = 0; i < line.length() && line.charAt( i ) == '\t'; i++ )
347         {
348             level++;
349         }
350         for ( int i = level + 1; i <= level + 4 && i < line.length(); i++ )
351         {
352             if ( line.charAt( i ) == '\t' )
353             {
354                 level++;
355                 break;
356             }
357         }
358         return level;
359     }
360 }