Coverage Report - org.apache.maven.plugin.pmd.PmdReport
 
Classes in this File Line Coverage Branch Coverage Complexity
PmdReport
71%
91/128
59%
26/44
2,682
PmdReport$ProcessingErrorRuleViolation
0%
0/16
N/A
2,682
 
 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.FileReader;
 26  
 import java.io.FileWriter;
 27  
 import java.io.IOException;
 28  
 import java.io.InputStream;
 29  
 import java.io.InputStreamReader;
 30  
 import java.io.Reader;
 31  
 import java.io.StringWriter;
 32  
 import java.io.UnsupportedEncodingException;
 33  
 import java.io.Writer;
 34  
 import java.util.Iterator;
 35  
 import java.util.Locale;
 36  
 import java.util.Map;
 37  
 import java.util.ResourceBundle;
 38  
 
 39  
 import net.sourceforge.pmd.IRuleViolation;
 40  
 import net.sourceforge.pmd.PMD;
 41  
 import net.sourceforge.pmd.PMDException;
 42  
 import net.sourceforge.pmd.Report;
 43  
 import net.sourceforge.pmd.Rule;
 44  
 import net.sourceforge.pmd.RuleContext;
 45  
 import net.sourceforge.pmd.RuleSet;
 46  
 import net.sourceforge.pmd.RuleSetFactory;
 47  
 import net.sourceforge.pmd.SourceType;
 48  
 import net.sourceforge.pmd.renderers.CSVRenderer;
 49  
 import net.sourceforge.pmd.renderers.HTMLRenderer;
 50  
 import net.sourceforge.pmd.renderers.Renderer;
 51  
 import net.sourceforge.pmd.renderers.TextRenderer;
 52  
 import net.sourceforge.pmd.renderers.XMLRenderer;
 53  
 
 54  
 import org.apache.maven.reporting.MavenReportException;
 55  
 import org.codehaus.doxia.sink.Sink;
 56  
 import org.codehaus.plexus.resource.ResourceManager;
 57  
 import org.codehaus.plexus.resource.loader.FileResourceCreationException;
 58  
 import org.codehaus.plexus.resource.loader.FileResourceLoader;
 59  
 import org.codehaus.plexus.resource.loader.ResourceNotFoundException;
 60  
 
 61  
 /**
 62  
  * Creates a PMD report.
 63  
  *
 64  
  * @author Brett Porter
 65  
  * @version $Id: PmdReport.java,v 1.3 2005/02/23 00:08:53 brett Exp $
 66  
  * @goal pmd
 67  
  * @todo needs to support the multiple source roots
 68  
  */
 69  7
 public class PmdReport
 70  
     extends AbstractPmdReport
 71  
 {
 72  
     /**
 73  
      * The target JDK to analyse based on. Should match the target used in the compiler plugin. Valid values are
 74  
      * currently <code>1.3</code>, <code>1.4</code>, <code>1.5</code> and <code>1.6</code>.
 75  
      * <p>
 76  
      * <b>Note:</b> support for <code>1.6</code> was added in version 2.3 of this plugin.
 77  
      * </p>
 78  
      * 
 79  
      * @parameter expression="${targetJdk}"
 80  
      */
 81  
     private String targetJdk;
 82  
 
 83  
     /**
 84  
      * The rule priority threshold; rules with lower priority
 85  
      * than this will not be evaluated.
 86  
      *
 87  
      * @parameter expression="${minimumPriority}" default-value="5"
 88  
      */
 89  7
     private int minimumPriority = 5;
 90  
 
 91  
     /**
 92  
      * Skip the PMD report generation.  Most useful on the command line
 93  
      * via "-Dpmd.skip=true".
 94  
      *
 95  
      * @parameter expression="${pmd.skip}" default-value="false"
 96  
      */
 97  
     private boolean skip;
 98  
 
 99  
     /**
 100  
      * The PMD rulesets to use. See the <a href="http://pmd.sourceforge.net/rules/index.html">Stock Rulesets</a> for a
 101  
      * list of some included. Defaults to the basic, imports and unusedcode rulesets.
 102  
      *
 103  
      * @parameter
 104  
      */
 105  7
     private String[] rulesets = new String[]{"rulesets/basic.xml", "rulesets/unusedcode.xml", "rulesets/imports.xml", };
 106  
 
 107  
     /**
 108  
      * The file encoding to use when reading the java source.
 109  
      *
 110  
      * @parameter
 111  
      */
 112  
     private String sourceEncoding;
 113  
 
 114  
     /**
 115  
      * @component
 116  
      * @required
 117  
      * @readonly
 118  
      */
 119  
     private ResourceManager locator;
 120  
     
 121  
     
 122  
     /** @see org.apache.maven.reporting.MavenReport#getName(java.util.Locale) */
 123  
     public String getName( Locale locale )
 124  
     {
 125  9
         return getBundle( locale ).getString( "report.pmd.name" );
 126  
     }
 127  
 
 128  
     /** @see org.apache.maven.reporting.MavenReport#getDescription(java.util.Locale) */
 129  
     public String getDescription( Locale locale )
 130  
     {
 131  0
         return getBundle( locale ).getString( "report.pmd.description" );
 132  
     }
 133  
 
 134  
     public void setRulesets( String[] rules )
 135  
     {
 136  2
         rulesets = rules;
 137  2
     }
 138  
     
 139  
     /** @see org.apache.maven.reporting.AbstractMavenReport#executeReport(java.util.Locale) */
 140  
     public void executeReport( Locale locale )
 141  
         throws MavenReportException
 142  
     {
 143  
         //configure ResourceManager
 144  7
         locator.addSearchPath( FileResourceLoader.ID, project.getFile().getParentFile().getAbsolutePath() );
 145  7
         locator.addSearchPath( "url", "" );
 146  7
         locator.setOutputDirectory( new File( project.getBuild().getDirectory() ) );
 147  
 
 148  7
         if ( !skip && canGenerateReport() )
 149  
         {
 150  5
             ClassLoader origLoader = Thread.currentThread().getContextClassLoader();
 151  
             try
 152  
             {
 153  5
                 Thread.currentThread().setContextClassLoader( this.getClass().getClassLoader() );
 154  
                 
 155  5
                 Sink sink = getSink();
 156  
     
 157  5
                 PMD pmd = getPMD();
 158  4
                 RuleContext ruleContext = new RuleContext();
 159  4
                 Report report = new Report();
 160  4
                 PmdReportListener reportSink = new PmdReportListener( sink, getBundle( locale ), aggregate );
 161  
     
 162  4
                 report.addListener( reportSink );
 163  4
                 ruleContext.setReport( report );
 164  4
                 reportSink.beginDocument();
 165  
     
 166  4
                 RuleSetFactory ruleSetFactory = new RuleSetFactory();
 167  4
                 ruleSetFactory.setMinimumPriority( this.minimumPriority );
 168  4
                 RuleSet[] sets = new RuleSet[rulesets.length];
 169  
                 try
 170  
                 {
 171  17
                     for ( int idx = 0; idx < rulesets.length; idx++ )
 172  
                     {
 173  13
                         String set = rulesets[idx];
 174  13
                         getLog().debug( "Preparing ruleset: " + set );
 175  13
                         File ruleset = null;
 176  13
                         ruleset = locator.getResourceAsFile( set, getLocationTemp( set ) );
 177  
                         
 178  13
                         if ( null == ruleset )
 179  
                         {
 180  0
                             throw new MavenReportException( "Could not resolve " + set );
 181  
                         }
 182  
     
 183  13
                         InputStream rulesInput = new FileInputStream( ruleset );
 184  13
                         sets[idx] = ruleSetFactory.createRuleSet( rulesInput );
 185  
                     }
 186  
                 }
 187  0
                 catch ( IOException e )
 188  
                 {
 189  0
                     throw new MavenReportException( e.getMessage(), e );
 190  
                 }
 191  0
                 catch ( ResourceNotFoundException e )
 192  
                 {
 193  0
                     throw new MavenReportException( e.getMessage(), e );
 194  
                 }
 195  0
                 catch ( FileResourceCreationException e )
 196  
                 {
 197  0
                     throw new MavenReportException( e.getMessage(), e );
 198  4
                 }
 199  
     
 200  4
                 boolean hasEncoding = sourceEncoding != null;
 201  
     
 202  
                 Map files;
 203  
                 try
 204  
                 {
 205  4
                     files = getFilesToProcess( );
 206  
                 }
 207  0
                 catch ( IOException e )
 208  
                 {
 209  0
                     throw new MavenReportException( "Can't get file list", e );
 210  4
                 }
 211  
     
 212  4
                 for ( Iterator i = files.entrySet().iterator(); i.hasNext(); )
 213  
                 {
 214  11
                     Map.Entry entry = (Map.Entry) i.next();
 215  11
                     File file = (File) entry.getKey();
 216  11
                     PmdFileInfo fileInfo = (PmdFileInfo) entry.getValue();
 217  
     
 218  
                     // TODO: lazily call beginFile in case there are no rules
 219  
     
 220  11
                     reportSink.beginFile( file , fileInfo );
 221  11
                     ruleContext.setSourceCodeFilename( file.getAbsolutePath() );
 222  46
                     for ( int idx = 0; idx < rulesets.length; idx++ )
 223  
                     {
 224  
                         try
 225  
                         {
 226  
                             // PMD closes this Reader even though it did not open it so we have
 227  
                             // to open a new one with every call to processFile().
 228  35
                             Reader reader = hasEncoding
 229  
                                 ? new InputStreamReader( new FileInputStream( file ), sourceEncoding )
 230  
                                 : new FileReader( file );
 231  35
                             pmd.processFile( reader, sets[idx], ruleContext );
 232  
                         }
 233  0
                         catch ( UnsupportedEncodingException e1 )
 234  
                         {
 235  0
                             throw new MavenReportException( "Encoding '" + sourceEncoding + "' is not supported.", e1 );
 236  
                         }
 237  0
                         catch ( PMDException pe )
 238  
                         {
 239  0
                             String msg = pe.getLocalizedMessage();
 240  0
                             Exception r = pe.getReason();
 241  0
                             if ( r != null )
 242  
                             {
 243  0
                                 msg = msg + ": " + r.getLocalizedMessage();
 244  
                             }
 245  0
                             getLog().warn( msg );
 246  0
                             reportSink.ruleViolationAdded(
 247  
                                 new ProcessingErrorRuleViolation( file, msg ) );
 248  
                         }
 249  0
                         catch ( FileNotFoundException e2 )
 250  
                         {
 251  0
                             getLog().warn( "Error opening source file: " + file );
 252  0
                             reportSink.ruleViolationAdded(
 253  
                                 new ProcessingErrorRuleViolation( file, e2.getLocalizedMessage() ) );
 254  
                         }
 255  0
                         catch ( Exception e3 )
 256  
                         {
 257  0
                             getLog().warn( "Failure executing PMD for: " + file, e3 );
 258  0
                             reportSink.ruleViolationAdded(
 259  
                                 new ProcessingErrorRuleViolation( file, e3.getLocalizedMessage() ) );
 260  35
                         }
 261  
                     }
 262  11
                     reportSink.endFile( file );
 263  
                 }
 264  
     
 265  4
                 reportSink.endDocument();
 266  
     
 267  4
                 if ( !isHtml() )
 268  
                 {
 269  
                     // Use the PMD renderers to render in any format aside from HTML.
 270  4
                     Renderer r = createRenderer();
 271  4
                     StringWriter stringwriter = new StringWriter();
 272  
                     
 273  
                     try
 274  
                     {
 275  4
                         r.setWriter( stringwriter );
 276  4
                         r.start();
 277  4
                         r.renderFileReport( report );
 278  4
                         r.end();
 279  4
                         String buffer = stringwriter.toString();
 280  
                         
 281  4
                         Writer writer = new FileWriter( new File( targetDirectory, "pmd." + format ) );
 282  4
                         writer.write( buffer, 0, buffer.length() );
 283  4
                         writer.close();
 284  
     
 285  4
                         File siteDir = new File( targetDirectory, "site" );
 286  4
                         siteDir.mkdirs();
 287  4
                         writer = new FileWriter( new File( siteDir,
 288  
                                                              "pmd." + format ) );
 289  4
                         writer.write( buffer, 0, buffer.length() );
 290  4
                         writer.close();
 291  
                     }
 292  0
                     catch ( IOException ioe )
 293  
                     {
 294  0
                         throw new MavenReportException( ioe.getMessage(), ioe );
 295  4
                     }
 296  
                 }
 297  
             }
 298  
             finally
 299  
             {
 300  5
                 Thread.currentThread().setContextClassLoader( origLoader );
 301  4
             }
 302  
         }
 303  5
     }
 304  
 
 305  
     /**
 306  
      * Convenience method to get the location of the specified file name.
 307  
      *
 308  
      * @param name the name of the file whose location is to be resolved
 309  
      * @return a String that contains the absolute file name of the file
 310  
      */
 311  
     private String getLocationTemp( String name )
 312  
     {
 313  13
         String loc = name;
 314  13
         if ( loc.indexOf( '/' ) != -1 )
 315  
         {
 316  13
             loc = loc.substring( loc.lastIndexOf( '/' ) + 1 );
 317  
         }
 318  13
         if ( loc.indexOf( '\\' ) != -1 )
 319  
         {
 320  0
             loc = loc.substring( loc.lastIndexOf( '\\' ) + 1 );
 321  
         }
 322  13
         getLog().debug( "Before: " + name + " After: " + loc );
 323  13
         return loc;
 324  
     }
 325  
 
 326  
     /**
 327  
      * Constructs the PMD class, passing it an argument
 328  
      * that configures the target JDK.
 329  
      *
 330  
      * @return the resulting PMD
 331  
      * @throws org.apache.maven.reporting.MavenReportException
 332  
      *          if targetJdk is not supported
 333  
      */
 334  
     public PMD getPMD()
 335  
         throws MavenReportException
 336  
     {
 337  5
         PMD pmd = new PMD();
 338  
 
 339  5
         if ( null != targetJdk )
 340  
         {
 341  2
             SourceType sourceType = SourceType.getSourceTypeForId( "java " + targetJdk );
 342  2
             if ( sourceType == null )
 343  
             {
 344  1
                 throw new MavenReportException( "Unsupported targetJdk value '" + targetJdk + "'." );
 345  
             }
 346  1
             pmd.setJavaVersion( sourceType );
 347  
         }
 348  
 
 349  4
         return pmd;
 350  
     }
 351  
 
 352  
     /** @see org.apache.maven.reporting.MavenReport#getOutputName() */
 353  
     public String getOutputName()
 354  
     {
 355  14
         return "pmd";
 356  
     }
 357  
 
 358  
     private static ResourceBundle getBundle( Locale locale )
 359  
     {
 360  14
         return ResourceBundle.getBundle( "pmd-report", locale, PmdReport.class.getClassLoader() );
 361  
     }
 362  
 
 363  
     /**
 364  
      * Create and return the correct renderer for the output type.
 365  
      *
 366  
      * @return the renderer based on the configured output
 367  
      * @throws org.apache.maven.reporting.MavenReportException
 368  
      *          if no renderer found for the output type
 369  
      */
 370  
     public final Renderer createRenderer()
 371  
         throws MavenReportException
 372  
     {
 373  4
         Renderer renderer = null;
 374  4
         if ( "xml".equals( format ) )
 375  
         {
 376  3
             renderer = new XMLRenderer();
 377  
         }
 378  1
         else if ( "txt".equals( format ) )
 379  
         {
 380  0
             renderer = new TextRenderer();
 381  
         }
 382  1
         else if ( "csv".equals( format ) )
 383  
         {
 384  1
             renderer = new CSVRenderer();
 385  
         }
 386  0
         else if ( "html".equals( format ) )
 387  
         {
 388  0
             renderer = new HTMLRenderer();
 389  
         }
 390  0
         else if ( !"".equals( format ) && !"none".equals( format ) )
 391  
         {
 392  
             try
 393  
             {
 394  0
                 renderer = (Renderer) Class.forName( format ).newInstance();
 395  
             }
 396  0
             catch ( Exception e )
 397  
             {
 398  0
                 throw new MavenReportException(
 399  
                     "Can't find the custom format " + format + ": " + e.getClass().getName() );
 400  0
             }
 401  
         }
 402  
 
 403  4
         if ( renderer == null )
 404  
         {
 405  0
             throw new MavenReportException( "Can't create report with format of " + format );
 406  
         }
 407  
 
 408  4
         return renderer;
 409  
     }
 410  
 
 411  
     /** @author <a href="mailto:douglass.doug@gmail.com">Doug Douglass</a> */
 412  7
     private static class ProcessingErrorRuleViolation
 413  
         implements IRuleViolation
 414  
     {
 415  
 
 416  
         private String filename;
 417  
 
 418  
         private String description;
 419  
 
 420  
         public ProcessingErrorRuleViolation( File file,
 421  
                                              String description )
 422  0
         {
 423  0
             filename = file.getPath();
 424  0
             this.description = description;
 425  0
         }
 426  
 
 427  
         public String getFilename()
 428  
         {
 429  0
             return this.filename;
 430  
         }
 431  
 
 432  
         public int getBeginLine()
 433  
         {
 434  0
             return 0;
 435  
         }
 436  
 
 437  
         public int getBeginColumn()
 438  
         {
 439  0
             return 0;
 440  
         }
 441  
 
 442  
         public int getEndLine()
 443  
         {
 444  0
             return 0;
 445  
         }
 446  
 
 447  
         public int getEndColumn()
 448  
         {
 449  0
             return 0;
 450  
         }
 451  
 
 452  
         public Rule getRule()
 453  
         {
 454  0
             return null;
 455  
         }
 456  
 
 457  
         public String getDescription()
 458  
         {
 459  0
             return this.description;
 460  
         }
 461  
 
 462  
         public String getPackageName()
 463  
         {
 464  0
             return null;
 465  
         }
 466  
 
 467  
         public String getMethodName()
 468  
         {
 469  0
             return null;
 470  
         }
 471  
 
 472  
         public String getClassName()
 473  
         {
 474  0
             return null;
 475  
         }
 476  
 
 477  
         public boolean isSuppressed()
 478  
         {
 479  0
             return false;
 480  
         }
 481  
 
 482  
         public String getVariableName()
 483  
         {
 484  0
             return null;
 485  
         }
 486  
     }
 487  
 }