Coverage Report - org.apache.maven.plugin.pmd.PmdReport
 
Classes in this File Line Coverage Branch Coverage Complexity
PmdReport
73 %
107/146
58 %
29/50
2,577
PmdReport$PmdXMLRenderer
100 %
3/3
N/A
2,577
PmdReport$ProcessingErrorRuleViolation
0 %
0/16
N/A
2,577
 
 1  
 package org.apache.maven.plugin.pmd;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  *   http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 20  
  */
 21  
 
 22  
 import java.io.File;
 23  
 import java.io.FileInputStream;
 24  
 import java.io.FileNotFoundException;
 25  
 import java.io.FileOutputStream;
 26  
 import java.io.IOException;
 27  
 import java.io.InputStream;
 28  
 import java.io.OutputStreamWriter;
 29  
 import java.io.Reader;
 30  
 import java.io.UnsupportedEncodingException;
 31  
 import java.io.Writer;
 32  
 import java.util.Iterator;
 33  
 import java.util.Locale;
 34  
 import java.util.Map;
 35  
 import java.util.ResourceBundle;
 36  
 
 37  
 import net.sourceforge.pmd.IRuleViolation;
 38  
 import net.sourceforge.pmd.PMD;
 39  
 import net.sourceforge.pmd.PMDException;
 40  
 import net.sourceforge.pmd.Report;
 41  
 import net.sourceforge.pmd.Rule;
 42  
 import net.sourceforge.pmd.RuleContext;
 43  
 import net.sourceforge.pmd.RuleSet;
 44  
 import net.sourceforge.pmd.RuleSetFactory;
 45  
 import net.sourceforge.pmd.SourceType;
 46  
 import net.sourceforge.pmd.renderers.CSVRenderer;
 47  
 import net.sourceforge.pmd.renderers.HTMLRenderer;
 48  
 import net.sourceforge.pmd.renderers.Renderer;
 49  
 import net.sourceforge.pmd.renderers.TextRenderer;
 50  
 import net.sourceforge.pmd.renderers.XMLRenderer;
 51  
 
 52  
 import org.apache.maven.doxia.sink.Sink;
 53  
 import org.apache.maven.reporting.MavenReportException;
 54  
 import org.codehaus.plexus.resource.ResourceManager;
 55  
 import org.codehaus.plexus.resource.loader.FileResourceCreationException;
 56  
 import org.codehaus.plexus.resource.loader.FileResourceLoader;
 57  
 import org.codehaus.plexus.resource.loader.ResourceNotFoundException;
 58  
 import org.codehaus.plexus.util.FileUtils;
 59  
 import org.codehaus.plexus.util.IOUtil;
 60  
 import org.codehaus.plexus.util.ReaderFactory;
 61  
 import org.codehaus.plexus.util.StringUtils;
 62  
 
 63  
 /**
 64  
  * Creates a PMD report.
 65  
  *
 66  
  * @author Brett Porter
 67  
  * @version $Id$
 68  
  * @since 2.0
 69  
  * @goal pmd
 70  
  */
 71  14
 public class PmdReport
 72  
     extends AbstractPmdReport
 73  
 {
 74  
     /**
 75  
      * The target JDK to analyze based on. Should match the target used in the compiler plugin. Valid values are
 76  
      * currently <code>1.3</code>, <code>1.4</code>, <code>1.5</code> and <code>1.6</code>.
 77  
      * <p>
 78  
      * <b>Note:</b> support for <code>1.6</code> was added in version 2.3 of this plugin.
 79  
      * </p>
 80  
      *
 81  
      * @parameter expression="${targetJdk}"
 82  
      */
 83  
     private String targetJdk;
 84  
 
 85  
     /**
 86  
      * The rule priority threshold; rules with lower priority
 87  
      * than this will not be evaluated.
 88  
      *
 89  
      * @parameter expression="${minimumPriority}" default-value="5"
 90  
      * @since 2.1
 91  
      */
 92  14
     private int minimumPriority = 5;
 93  
 
 94  
     /**
 95  
      * Skip the PMD report generation.  Most useful on the command line
 96  
      * via "-Dpmd.skip=true".
 97  
      *
 98  
      * @parameter expression="${pmd.skip}" default-value="false"
 99  
      * @since 2.1
 100  
      */
 101  
     private boolean skip;
 102  
 
 103  
     /**
 104  
      * The PMD rulesets to use. See the <a href="http://pmd.sourceforge.net/rules/index.html">Stock Rulesets</a> for a
 105  
      * list of some included. Since version 2.5, the ruleset "rulesets/maven.xml" is also available. Defaults to the
 106  
      * basic, imports and unusedcode rulesets.
 107  
      *
 108  
      * @parameter
 109  
      */
 110  14
     private String[] rulesets = new String[]{"rulesets/basic.xml", "rulesets/unusedcode.xml", "rulesets/imports.xml", };
 111  
 
 112  
     /**
 113  
      * @component
 114  
      * @required
 115  
      * @readonly
 116  
      */
 117  
     private ResourceManager locator;
 118  
 
 119  
     /** {@inheritDoc} */
 120  
     public String getName( Locale locale )
 121  
     {
 122  2
         return getBundle( locale ).getString( "report.pmd.name" );
 123  
     }
 124  
 
 125  
     /** {@inheritDoc} */
 126  
     public String getDescription( Locale locale )
 127  
     {
 128  0
         return getBundle( locale ).getString( "report.pmd.description" );
 129  
     }
 130  
 
 131  
     public void setRulesets( String[] rules )
 132  
     {
 133  4
         rulesets = rules;
 134  4
     }
 135  
 
 136  
     /** {@inheritDoc} */
 137  
     public void executeReport( Locale locale )
 138  
         throws MavenReportException
 139  
     {
 140  
         try
 141  
         {
 142  14
             execute( locale );
 143  
         }
 144  
         finally
 145  
         {
 146  14
             if ( getSink() != null )
 147  
             {
 148  14
                 getSink().close();
 149  
             }
 150  
         }
 151  10
     }
 152  
 
 153  
     private void execute( Locale locale )
 154  
         throws MavenReportException
 155  
     {
 156  
         //configure ResourceManager
 157  14
         locator.addSearchPath( FileResourceLoader.ID, project.getFile().getParentFile().getAbsolutePath() );
 158  14
         locator.addSearchPath( "url", "" );
 159  14
         locator.setOutputDirectory( new File( project.getBuild().getDirectory() ) );
 160  
 
 161  14
         if ( !skip && canGenerateReport() )
 162  
         {
 163  10
             ClassLoader origLoader = Thread.currentThread().getContextClassLoader();
 164  
             try
 165  
             {
 166  10
                 Thread.currentThread().setContextClassLoader( this.getClass().getClassLoader() );
 167  
 
 168  10
                 Report report = generateReport( locale );
 169  
 
 170  8
                 if ( !isHtml() )
 171  
                 {
 172  8
                     writeNonHtml( report );
 173  
                 }
 174  
             }
 175  
             finally
 176  
             {
 177  10
                 Thread.currentThread().setContextClassLoader( origLoader );
 178  8
             }
 179  
         }
 180  10
     }
 181  
 
 182  
     private Report generateReport( Locale locale )
 183  
         throws MavenReportException
 184  
     {
 185  10
         Sink sink = getSink();
 186  
 
 187  10
         PMD pmd = getPMD();
 188  8
         RuleContext ruleContext = new RuleContext();
 189  8
         Report report = new Report();
 190  8
         PmdReportListener reportSink = new PmdReportListener( sink, getBundle( locale ), aggregate );
 191  
 
 192  8
         report.addListener( reportSink );
 193  8
         ruleContext.setReport( report );
 194  8
         reportSink.beginDocument();
 195  
 
 196  8
         RuleSetFactory ruleSetFactory = new RuleSetFactory();
 197  8
         ruleSetFactory.setMinimumPriority( this.minimumPriority );
 198  8
         RuleSet[] sets = new RuleSet[rulesets.length];
 199  
         try
 200  
         {
 201  34
             for ( int idx = 0; idx < rulesets.length; idx++ )
 202  
             {
 203  26
                 String set = rulesets[idx];
 204  26
                 getLog().debug( "Preparing ruleset: " + set );
 205  26
                 File ruleset = locator.getResourceAsFile( set, getLocationTemp( set ) );
 206  
 
 207  26
                 if ( null == ruleset )
 208  
                 {
 209  0
                     throw new MavenReportException( "Could not resolve " + set );
 210  
                 }
 211  
 
 212  26
                 InputStream rulesInput = new FileInputStream( ruleset );
 213  
                 try
 214  
                 {
 215  26
                     RuleSet ruleSet = ruleSetFactory.createRuleSet( rulesInput );
 216  26
                     sets[idx] = ruleSet;
 217  
 
 218  26
                     ruleSet.start( ruleContext );
 219  
                 }
 220  
                 finally
 221  
                 {
 222  26
                     rulesInput.close();
 223  26
                 }
 224  
             }
 225  
         }
 226  0
         catch ( IOException e )
 227  
         {
 228  0
             throw new MavenReportException( e.getMessage(), e );
 229  
         }
 230  0
         catch ( ResourceNotFoundException e )
 231  
         {
 232  0
             throw new MavenReportException( e.getMessage(), e );
 233  
         }
 234  0
         catch ( FileResourceCreationException e )
 235  
         {
 236  0
             throw new MavenReportException( e.getMessage(), e );
 237  8
         }
 238  
 
 239  
         Map files;
 240  
         try
 241  
         {
 242  8
             files = getFilesToProcess( );
 243  
         }
 244  0
         catch ( IOException e )
 245  
         {
 246  0
             throw new MavenReportException( "Can't get file list", e );
 247  8
         }
 248  
 
 249  8
         if ( StringUtils.isEmpty( getSourceEncoding() ) && !files.isEmpty() )
 250  
         {
 251  0
             getLog().warn( "File encoding has not been set, using platform encoding "
 252  
                                + ReaderFactory.FILE_ENCODING + ", i.e. build is platform dependent!" );
 253  
         }
 254  
 
 255  8
         for ( Iterator i = files.entrySet().iterator(); i.hasNext(); )
 256  
         {
 257  22
             Map.Entry entry = (Map.Entry) i.next();
 258  22
             File file = (File) entry.getKey();
 259  22
             PmdFileInfo fileInfo = (PmdFileInfo) entry.getValue();
 260  
 
 261  
             // TODO: lazily call beginFile in case there are no rules
 262  
 
 263  22
             reportSink.beginFile( file , fileInfo );
 264  22
             ruleContext.setSourceCodeFilename( file.getAbsolutePath() );
 265  92
             for ( int idx = 0; idx < rulesets.length; idx++ )
 266  
             {
 267  
                 try
 268  
                 {
 269  
                     // PMD closes this Reader even though it did not open it so we have
 270  
                     // to open a new one with every call to processFile().
 271  
                     Reader reader;
 272  70
                     if ( StringUtils.isNotEmpty( getSourceEncoding() ) )
 273  
                     {
 274  70
                         reader = ReaderFactory.newReader( file, getSourceEncoding() );
 275  
                     }
 276  
                     else
 277  
                     {
 278  0
                         reader = ReaderFactory.newPlatformReader( file );
 279  
                     }
 280  
 
 281  
                     try
 282  
                     {
 283  70
                         pmd.processFile( reader, sets[idx], ruleContext );
 284  
                     }
 285  
                     finally
 286  
                     {
 287  70
                         reader.close();
 288  70
                     }
 289  
                 }
 290  0
                 catch ( UnsupportedEncodingException e1 )
 291  
                 {
 292  0
                     throw new MavenReportException( "Encoding '" + getSourceEncoding() + "' is not supported.", e1 );
 293  
                 }
 294  0
                 catch ( PMDException pe )
 295  
                 {
 296  0
                     String msg = pe.getLocalizedMessage();
 297  0
                     Throwable r = pe.getCause();
 298  0
                     if ( r != null )
 299  
                     {
 300  0
                         msg = msg + ": " + r.getLocalizedMessage();
 301  
                     }
 302  0
                     getLog().warn( msg );
 303  0
                     reportSink.ruleViolationAdded( new ProcessingErrorRuleViolation( file, msg ) );
 304  
                 }
 305  0
                 catch ( FileNotFoundException e2 )
 306  
                 {
 307  0
                     getLog().warn( "Error opening source file: " + file );
 308  0
                     reportSink.ruleViolationAdded(
 309  
                         new ProcessingErrorRuleViolation( file, e2.getLocalizedMessage() ) );
 310  
                 }
 311  0
                 catch ( Exception e3 )
 312  
                 {
 313  0
                     getLog().warn( "Failure executing PMD for: " + file, e3 );
 314  0
                     reportSink.ruleViolationAdded(
 315  
                         new ProcessingErrorRuleViolation( file, e3.getLocalizedMessage() ) );
 316  70
                 }
 317  
             }
 318  22
             reportSink.endFile( file );
 319  22
         }
 320  
 
 321  34
         for ( int idx = 0; idx < rulesets.length; idx++ )
 322  
         {
 323  26
             sets[idx].end( ruleContext );
 324  
         }
 325  
 
 326  8
         reportSink.endDocument();
 327  
 
 328  8
         return report;
 329  
     }
 330  
 
 331  
     /**
 332  
      * Use the PMD renderers to render in any format aside from HTML.
 333  
      *
 334  
      * @param report
 335  
      * @throws MavenReportException
 336  
      */
 337  
     private void writeNonHtml( Report report )
 338  
         throws MavenReportException
 339  
     {
 340  8
         Renderer r = createRenderer();
 341  
 
 342  8
         if ( r == null )
 343  
         {
 344  0
             return;
 345  
         }
 346  
 
 347  8
         Writer writer = null;
 348  
 
 349  
         try
 350  
         {
 351  8
             File targetFile = new File( targetDirectory, "pmd." + format );
 352  8
             FileOutputStream tStream = new FileOutputStream( targetFile );
 353  8
             writer = new OutputStreamWriter( tStream, getOutputEncoding() );
 354  
 
 355  8
             r.setWriter( writer );
 356  8
             r.start();
 357  8
             r.renderFileReport( report );
 358  8
             r.end();
 359  8
             writer.close();
 360  
 
 361  8
             File siteDir = getReportOutputDirectory();
 362  8
             siteDir.mkdirs();
 363  8
             FileUtils.copyFile( targetFile, new File( siteDir, "pmd." + format ) );
 364  
         }
 365  0
         catch ( IOException ioe )
 366  
         {
 367  0
             throw new MavenReportException( ioe.getMessage(), ioe );
 368  
         }
 369  
         finally
 370  
         {
 371  8
             IOUtil.close( writer );
 372  8
         }
 373  8
     }
 374  
 
 375  
     /**
 376  
      * Convenience method to get the location of the specified file name.
 377  
      *
 378  
      * @param name the name of the file whose location is to be resolved
 379  
      * @return a String that contains the absolute file name of the file
 380  
      */
 381  
     private String getLocationTemp( String name )
 382  
     {
 383  26
         String loc = name;
 384  26
         if ( loc.indexOf( '/' ) != -1 )
 385  
         {
 386  26
             loc = loc.substring( loc.lastIndexOf( '/' ) + 1 );
 387  
         }
 388  26
         if ( loc.indexOf( '\\' ) != -1 )
 389  
         {
 390  0
             loc = loc.substring( loc.lastIndexOf( '\\' ) + 1 );
 391  
         }
 392  26
         getLog().debug( "Before: " + name + " After: " + loc );
 393  26
         return loc;
 394  
     }
 395  
 
 396  
     /**
 397  
      * Constructs the PMD class, passing it an argument
 398  
      * that configures the target JDK.
 399  
      *
 400  
      * @return the resulting PMD
 401  
      * @throws org.apache.maven.reporting.MavenReportException
 402  
      *          if targetJdk is not supported
 403  
      */
 404  
     public PMD getPMD()
 405  
         throws MavenReportException
 406  
     {
 407  10
         PMD pmd = new PMD();
 408  
 
 409  10
         if ( null != targetJdk )
 410  
         {
 411  4
             SourceType sourceType = SourceType.getSourceTypeForId( "java " + targetJdk );
 412  4
             if ( sourceType == null )
 413  
             {
 414  2
                 throw new MavenReportException( "Unsupported targetJdk value '" + targetJdk + "'." );
 415  
             }
 416  2
             pmd.setJavaVersion( sourceType );
 417  
         }
 418  
 
 419  8
         return pmd;
 420  
     }
 421  
 
 422  
     /** {@inheritDoc} */
 423  
     public String getOutputName()
 424  
     {
 425  24
         return "pmd";
 426  
     }
 427  
 
 428  
     private static ResourceBundle getBundle( Locale locale )
 429  
     {
 430  12
         return ResourceBundle.getBundle( "pmd-report", locale, PmdReport.class.getClassLoader() );
 431  
     }
 432  
 
 433  
     /**
 434  
      * Create and return the correct renderer for the output type.
 435  
      *
 436  
      * @return the renderer based on the configured output
 437  
      * @throws org.apache.maven.reporting.MavenReportException
 438  
      *          if no renderer found for the output type
 439  
      */
 440  
     public final Renderer createRenderer()
 441  
         throws MavenReportException
 442  
     {
 443  8
         Renderer renderer = null;
 444  8
         if ( "xml".equals( format ) )
 445  
         {
 446  6
             renderer = new PmdXMLRenderer( getOutputEncoding() );
 447  
         }
 448  2
         else if ( "txt".equals( format ) )
 449  
         {
 450  0
             renderer = new TextRenderer();
 451  
         }
 452  2
         else if ( "csv".equals( format ) )
 453  
         {
 454  2
             renderer = new CSVRenderer();
 455  
         }
 456  0
         else if ( "html".equals( format ) )
 457  
         {
 458  0
             renderer = new HTMLRenderer();
 459  
         }
 460  0
         else if ( !"".equals( format ) && !"none".equals( format ) )
 461  
         {
 462  
             try
 463  
             {
 464  0
                 renderer = (Renderer) Class.forName( format ).newInstance();
 465  
             }
 466  0
             catch ( Exception e )
 467  
             {
 468  0
                 throw new MavenReportException(
 469  
                     "Can't find PMD custom format " + format + ": " + e.getClass().getName(), e );
 470  0
             }
 471  
         }
 472  
 
 473  8
         return renderer;
 474  
     }
 475  
 
 476  
     private static class PmdXMLRenderer extends XMLRenderer
 477  
     {
 478  
         public PmdXMLRenderer( String encoding )
 479  
         {
 480  6
             super();
 481  6
             this.encoding = encoding;
 482  6
         }
 483  
     }
 484  
 
 485  
     /** @author <a href="mailto:douglass.doug@gmail.com">Doug Douglass</a> */
 486  14
     private static class ProcessingErrorRuleViolation
 487  
         implements IRuleViolation
 488  
     {
 489  
 
 490  
         private String filename;
 491  
 
 492  
         private String description;
 493  
 
 494  
         public ProcessingErrorRuleViolation( File file,
 495  
                                              String description )
 496  0
         {
 497  0
             filename = file.getPath();
 498  0
             this.description = description;
 499  0
         }
 500  
 
 501  
         /** {@inheritDoc} */
 502  
         public String getFilename()
 503  
         {
 504  0
             return this.filename;
 505  
         }
 506  
 
 507  
         /** {@inheritDoc} */
 508  
         public int getBeginLine()
 509  
         {
 510  0
             return 0;
 511  
         }
 512  
 
 513  
         /** {@inheritDoc} */
 514  
         public int getBeginColumn()
 515  
         {
 516  0
             return 0;
 517  
         }
 518  
 
 519  
         /** {@inheritDoc} */
 520  
         public int getEndLine()
 521  
         {
 522  0
             return 0;
 523  
         }
 524  
 
 525  
         /** {@inheritDoc} */
 526  
         public int getEndColumn()
 527  
         {
 528  0
             return 0;
 529  
         }
 530  
 
 531  
         /** {@inheritDoc} */
 532  
         public Rule getRule()
 533  
         {
 534  0
             return null;
 535  
         }
 536  
 
 537  
         /** {@inheritDoc} */
 538  
         public String getDescription()
 539  
         {
 540  0
             return this.description;
 541  
         }
 542  
 
 543  
         /** {@inheritDoc} */
 544  
         public String getPackageName()
 545  
         {
 546  0
             return null;
 547  
         }
 548  
 
 549  
         /** {@inheritDoc} */
 550  
         public String getMethodName()
 551  
         {
 552  0
             return null;
 553  
         }
 554  
 
 555  
         /** {@inheritDoc} */
 556  
         public String getClassName()
 557  
         {
 558  0
             return null;
 559  
         }
 560  
 
 561  
         /** {@inheritDoc} */
 562  
         public boolean isSuppressed()
 563  
         {
 564  0
             return false;
 565  
         }
 566  
 
 567  
         /** {@inheritDoc} */
 568  
         public String getVariableName()
 569  
         {
 570  0
             return null;
 571  
         }
 572  
     }
 573  
 }