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