1
2 package org.nuiton.config;
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 import org.apache.maven.plugin.AbstractMojo;
27 import org.apache.maven.plugin.MojoExecutionException;
28 import org.apache.maven.plugins.annotations.Mojo;
29 import org.apache.maven.plugins.annotations.Parameter;
30
31 import org.w3c.dom.Document;
32 import org.w3c.dom.Element;
33 import org.w3c.dom.Node;
34 import org.w3c.dom.NodeList;
35 import org.xml.sax.SAXException;
36
37 import javax.xml.parsers.DocumentBuilder;
38 import javax.xml.parsers.DocumentBuilderFactory;
39 import javax.xml.parsers.ParserConfigurationException;
40 import java.io.IOException;
41 import java.io.InputStream;
42 import java.util.ArrayList;
43 import java.util.List;
44
45
46
47
48
49
50 @Mojo( name = "help", requiresProject = false, threadSafe = true )
51 public class HelpMojo
52 extends AbstractMojo
53 {
54
55
56
57
58 @Parameter( property = "detail", defaultValue = "false" )
59 private boolean detail;
60
61
62
63
64
65 @Parameter( property = "goal" )
66 private java.lang.String goal;
67
68
69
70
71
72 @Parameter( property = "lineLength", defaultValue = "80" )
73 private int lineLength;
74
75
76
77
78
79 @Parameter( property = "indentSize", defaultValue = "2" )
80 private int indentSize;
81
82
83 private static final String PLUGIN_HELP_PATH =
84 "/META-INF/maven/org.nuiton/nuiton-maven-report-plugin/plugin-help.xml";
85
86 private static final int DEFAULT_LINE_LENGTH = 80;
87
88 private Document build()
89 throws MojoExecutionException
90 {
91 getLog().debug( "load plugin-help.xml: " + PLUGIN_HELP_PATH );
92 InputStream is = null;
93 try
94 {
95 is = getClass().getResourceAsStream( PLUGIN_HELP_PATH );
96 DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
97 DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
98 return dBuilder.parse( is );
99 }
100 catch ( IOException e )
101 {
102 throw new MojoExecutionException( e.getMessage(), e );
103 }
104 catch ( ParserConfigurationException e )
105 {
106 throw new MojoExecutionException( e.getMessage(), e );
107 }
108 catch ( SAXException e )
109 {
110 throw new MojoExecutionException( e.getMessage(), e );
111 }
112 finally
113 {
114 if ( is != null )
115 {
116 try
117 {
118 is.close();
119 }
120 catch ( IOException e )
121 {
122 throw new MojoExecutionException( e.getMessage(), e );
123 }
124 }
125 }
126 }
127
128
129
130
131 public void execute()
132 throws MojoExecutionException
133 {
134 if ( lineLength <= 0 )
135 {
136 getLog().warn( "The parameter 'lineLength' should be positive, using '80' as default." );
137 lineLength = DEFAULT_LINE_LENGTH;
138 }
139 if ( indentSize <= 0 )
140 {
141 getLog().warn( "The parameter 'indentSize' should be positive, using '2' as default." );
142 indentSize = 2;
143 }
144
145 Document doc = build();
146
147 StringBuilder sb = new StringBuilder();
148 Node plugin = getSingleChild( doc, "plugin" );
149
150
151 String name = getValue( plugin, "name" );
152 String version = getValue( plugin, "version" );
153 String id = getValue( plugin, "groupId" ) + ":" + getValue( plugin, "artifactId" ) + ":" + version;
154 if ( isNotEmpty( name ) && !name.contains( id ) )
155 {
156 append( sb, name + " " + version, 0 );
157 }
158 else
159 {
160 if ( isNotEmpty( name ) )
161 {
162 append( sb, name, 0 );
163 }
164 else
165 {
166 append( sb, id, 0 );
167 }
168 }
169 append( sb, getValue( plugin, "description" ), 1 );
170 append( sb, "", 0 );
171
172
173 String goalPrefix = getValue( plugin, "goalPrefix" );
174
175 Node mojos1 = getSingleChild( plugin, "mojos" );
176
177 List<Node> mojos = findNamedChild( mojos1, "mojo" );
178
179 if ( goal == null || goal.length() <= 0 )
180 {
181 append( sb, "This plugin has " + mojos.size() + ( mojos.size() > 1 ? " goals:" : " goal:" ), 0 );
182 append( sb, "", 0 );
183 }
184
185 for ( Node mojo : mojos )
186 {
187 writeGoal( sb, goalPrefix, (Element) mojo );
188 }
189
190 if ( getLog().isInfoEnabled() )
191 {
192 getLog().info( sb.toString() );
193 }
194 }
195
196
197 private static boolean isNotEmpty( String string )
198 {
199 return string != null && string.length() > 0;
200 }
201
202 private String getValue( Node node, String elementName )
203 throws MojoExecutionException
204 {
205 return getSingleChild( node, elementName ).getTextContent();
206 }
207
208 private Node getSingleChild( Node node, String elementName )
209 throws MojoExecutionException
210 {
211 List<Node> namedChild = findNamedChild( node, elementName );
212 if ( namedChild.isEmpty() )
213 {
214 throw new MojoExecutionException( "Could not find " + elementName + " in plugin-help.xml" );
215 }
216 if ( namedChild.size() > 1 )
217 {
218 throw new MojoExecutionException( "Multiple " + elementName + " in plugin-help.xml" );
219 }
220 return namedChild.get( 0 );
221 }
222
223 private List<Node> findNamedChild( Node node, String elementName )
224 {
225 List<Node> result = new ArrayList<Node>();
226 NodeList childNodes = node.getChildNodes();
227 for ( int i = 0; i < childNodes.getLength(); i++ )
228 {
229 Node item = childNodes.item( i );
230 if ( elementName.equals( item.getNodeName() ) )
231 {
232 result.add( item );
233 }
234 }
235 return result;
236 }
237
238 private Node findSingleChild( Node node, String elementName )
239 throws MojoExecutionException
240 {
241 List<Node> elementsByTagName = findNamedChild( node, elementName );
242 if ( elementsByTagName.isEmpty() )
243 {
244 return null;
245 }
246 if ( elementsByTagName.size() > 1 )
247 {
248 throw new MojoExecutionException( "Multiple " + elementName + "in plugin-help.xml" );
249 }
250 return elementsByTagName.get( 0 );
251 }
252
253 private void writeGoal( StringBuilder sb, String goalPrefix, Element mojo )
254 throws MojoExecutionException
255 {
256 String mojoGoal = getValue( mojo, "goal" );
257 Node configurationElement = findSingleChild( mojo, "configuration" );
258 Node description = findSingleChild( mojo, "description" );
259 if ( goal == null || goal.length() <= 0 || mojoGoal.equals( goal ) )
260 {
261 append( sb, goalPrefix + ":" + mojoGoal, 0 );
262 Node deprecated = findSingleChild( mojo, "deprecated" );
263 if ( ( deprecated != null ) && isNotEmpty( deprecated.getTextContent() ) )
264 {
265 append( sb, "Deprecated. " + deprecated.getTextContent(), 1 );
266 if ( detail && description != null )
267 {
268 append( sb, "", 0 );
269 append( sb, description.getTextContent(), 1 );
270 }
271 }
272 else if ( description != null )
273 {
274 append( sb, description.getTextContent(), 1 );
275 }
276 append( sb, "", 0 );
277
278 if ( detail )
279 {
280 Node parametersNode = getSingleChild( mojo, "parameters" );
281 List<Node> parameters = findNamedChild( parametersNode, "parameter" );
282 append( sb, "Available parameters:", 1 );
283 append( sb, "", 0 );
284
285 for ( Node parameter : parameters )
286 {
287 writeParameter( sb, parameter, configurationElement );
288 }
289 }
290 }
291 }
292
293 private void writeParameter( StringBuilder sb, Node parameter, Node configurationElement )
294 throws MojoExecutionException
295 {
296 String parameterName = getValue( parameter, "name" );
297 String parameterDescription = getValue( parameter, "description" );
298
299 Element fieldConfigurationElement = (Element) findSingleChild( configurationElement, parameterName );
300
301 String parameterDefaultValue = "";
302 if ( fieldConfigurationElement != null && fieldConfigurationElement.hasAttribute( "default-value" ) )
303 {
304 parameterDefaultValue = " (Default: " + fieldConfigurationElement.getAttribute( "default-value" ) + ")";
305 }
306 append( sb, parameterName + parameterDefaultValue, 2 );
307 Node deprecated = findSingleChild( parameter, "deprecated" );
308 if ( ( deprecated != null ) && isNotEmpty( deprecated.getTextContent() ) )
309 {
310 append( sb, "Deprecated. " + deprecated.getTextContent(), 3 );
311 append( sb, "", 0 );
312 }
313 append( sb, parameterDescription, 3 );
314 if ( "true".equals( getValue( parameter, "required" ) ) )
315 {
316 append( sb, "Required: Yes", 3 );
317 }
318 if ( ( fieldConfigurationElement != null ) && isNotEmpty( fieldConfigurationElement.getTextContent() ) )
319 {
320 String property = getPropertyFromExpression( fieldConfigurationElement.getTextContent() );
321 append( sb, "User property: " + property, 3 );
322 }
323
324 append( sb, "", 0 );
325 }
326
327
328
329
330
331
332
333
334
335
336 private static String repeat( String str, int repeat )
337 {
338 StringBuilder buffer = new StringBuilder( repeat * str.length() );
339
340 for ( int i = 0; i < repeat; i++ )
341 {
342 buffer.append( str );
343 }
344
345 return buffer.toString();
346 }
347
348
349
350
351
352
353
354
355
356 private void append( StringBuilder sb, String description, int indent )
357 {
358 for ( String line : toLines( description, indent, indentSize, lineLength ) )
359 {
360 sb.append( line ).append( '\n' );
361 }
362 }
363
364
365
366
367
368
369
370
371
372
373
374 private static List<String> toLines( String text, int indent, int indentSize, int lineLength )
375 {
376 List<String> lines = new ArrayList<String>();
377
378 String ind = repeat( "\t", indent );
379
380 String[] plainLines = text.split( "(\r\n)|(\r)|(\n)" );
381
382 for ( String plainLine : plainLines )
383 {
384 toLines( lines, ind + plainLine, indentSize, lineLength );
385 }
386
387 return lines;
388 }
389
390
391
392
393
394
395
396
397
398 private static void toLines( List<String> lines, String line, int indentSize, int lineLength )
399 {
400 int lineIndent = getIndentLevel( line );
401 StringBuilder buf = new StringBuilder( 256 );
402
403 String[] tokens = line.split( " +" );
404
405 for ( String token : tokens )
406 {
407 if ( buf.length() > 0 )
408 {
409 if ( buf.length() + token.length() >= lineLength )
410 {
411 lines.add( buf.toString() );
412 buf.setLength( 0 );
413 buf.append( repeat( " ", lineIndent * indentSize ) );
414 }
415 else
416 {
417 buf.append( ' ' );
418 }
419 }
420
421 for ( int j = 0; j < token.length(); j++ )
422 {
423 char c = token.charAt( j );
424 if ( c == '\t' )
425 {
426 buf.append( repeat( " ", indentSize - buf.length() % indentSize ) );
427 }
428 else if ( c == '\u00A0' )
429 {
430 buf.append( ' ' );
431 }
432 else
433 {
434 buf.append( c );
435 }
436 }
437 }
438 lines.add( buf.toString() );
439 }
440
441
442
443
444
445
446
447 private static int getIndentLevel( String line )
448 {
449 int level = 0;
450 for ( int i = 0; i < line.length() && line.charAt( i ) == '\t'; i++ )
451 {
452 level++;
453 }
454 for ( int i = level + 1; i <= level + 4 && i < line.length(); i++ )
455 {
456 if ( line.charAt( i ) == '\t' )
457 {
458 level++;
459 break;
460 }
461 }
462 return level;
463 }
464
465 private String getPropertyFromExpression( String expression )
466 {
467 if ( expression != null && expression.startsWith( "${" ) && expression.endsWith( "}" )
468 && !expression.substring( 2 ).contains( "${" ) )
469 {
470
471 return expression.substring( 2, expression.length() - 1 );
472 }
473
474 return null;
475 }
476 }