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