##### Coverage Report - org.owasp.dependencycheck.maven.HelpMojo

Classes in this File | Line Coverage | Branch Coverage | Complexity
HelpMojo |
0% | 0/148
0% | 0/106
| 5.266666666666667;5.267

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