View Javadoc

    1   /***
    2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
    3    */
    4   package net.sourceforge.pmd;
    5
    6   import net.sourceforge.pmd.ast.ParseException;
    7   import net.sourceforge.pmd.cpd.FileFinder;
    8   import net.sourceforge.pmd.cpd.SourceFileOrDirectoryFilter;
    9   import net.sourceforge.pmd.parsers.Parser;
    10  import net.sourceforge.pmd.renderers.Renderer;
    11  import net.sourceforge.pmd.sourcetypehandlers.SourceTypeHandler;
    12  import net.sourceforge.pmd.sourcetypehandlers.SourceTypeHandlerBroker;
    13
    14  import java.io.BufferedInputStream;
    15  import java.io.File;
    16  import java.io.FileNotFoundException;
    17  import java.io.IOException;
    18  import java.io.InputStream;
    19  import java.io.InputStreamReader;
    20  import java.io.Reader;
    21  import java.io.UnsupportedEncodingException;
    22  import java.util.ArrayList;
    23  import java.util.Enumeration;
    24  import java.util.Iterator;
    25  import java.util.List;
    26  import java.util.StringTokenizer;
    27  import java.util.zip.ZipEntry;
    28  import java.util.zip.ZipFile;
    29
    30  public class PMD {
    31      public static final String EOL = System.getProperty("line.separator", "\n");
    32      public static final String VERSION = "3.7";
    33
    34      private String excludeMarker = ExcludeLines.EXCLUDE_MARKER;
    35      private SourceTypeDiscoverer sourceTypeDiscoverer = new SourceTypeDiscoverer();
    36      private SourceTypeHandlerBroker sourceTypeHandlerBroker = new SourceTypeHandlerBroker();
    37
    38      public PMD() {
    39      }
    40
    41      /***
    42       * @param targetJDKVersion
    43       * @deprecated Use the no-args constructor and the setJavaVersion method instead
    44       */
    45      public PMD(TargetJDKVersion targetJDKVersion) {
    46          if (targetJDKVersion instanceof TargetJDK1_3) {
    47              setJavaVersion(SourceType.JAVA_13);
    48          } else if (targetJDKVersion instanceof TargetJDK1_5) {
    49              setJavaVersion(SourceType.JAVA_15);
    50          }
    51      }
    52
    53      /***
    54       * Processes the file read by the reader agains the rule set.
    55       *
    56       * @param reader   input stream reader
    57       * @param ruleSets set of rules to process against the file
    58       * @param ctx      context in which PMD is operating. This contains the Renderer and
    59       *                 whatnot
    60       * @throws PMDException if the input could not be parsed or processed
    61       */
    62      public void processFile(Reader reader, RuleSets ruleSets, RuleContext ctx)
    63              throws PMDException {
    64          SourceType sourceType = getSourceTypeOfFile(ctx.getSourceCodeFilename());
    65
    66          processFile(reader, ruleSets, ctx, sourceType);
    67      }
    68
    69      /***
    70       * Processes the file read by the reader agains the rule set.
    71       *
    72       * @param reader     input stream reader
    73       * @param ruleSets   set of rules to process against the file
    74       * @param ctx        context in which PMD is operating. This contains the Renderer and
    75       *                   whatnot
    76       * @param sourceType the SourceType of the source
    77       * @throws PMDException if the input could not be parsed or processed
    78       */
    79      public void processFile(Reader reader, RuleSets ruleSets, RuleContext ctx,
    80                              SourceType sourceType) throws PMDException {
    81          try {
    82              SourceTypeHandler sourceTypeHandler = sourceTypeHandlerBroker
    83                      .getVisitorsFactoryForSourceType(sourceType);
    84
    85              ExcludeLines excluder = new ExcludeLines(reader, excludeMarker);
    86              ctx.excludeLines(excluder.getLinesToExclude());
    87
    88              Parser parser = sourceTypeHandler.getParser();
    89              Object rootNode = parser.parse(excluder.getCopyReader());
    90              Thread.yield();
    91
    92              // TODO - move SymbolFacade traversal inside JavaParser.CompilationUnit()
    93              sourceTypeHandler.getSymbolFacade().start(rootNode);
    94
    95              Language language = SourceTypeToRuleLanguageMapper.getMappedLanguage(sourceType);
    96
    97              if (ruleSets.usesDFA(language)) {
    98                  sourceTypeHandler.getDataFlowFacade().start(rootNode);
    99              }
    100
    101             List acus = new ArrayList();
    102             acus.add(rootNode);
    103
    104             ruleSets.apply(acus, ctx, language);
    105         } catch (ParseException pe) {
    106             throw new PMDException("Error while parsing "
    107                     + ctx.getSourceCodeFilename(), pe);
    108         } catch (Exception e) {
    109             throw new PMDException("Error while processing "
    110                     + ctx.getSourceCodeFilename(), e);
    111         } finally {
    112             try {
    113                 reader.close();
    114             } catch (IOException e) {
    115                 throw new PMDException("Error while closing "
    116                         + ctx.getSourceCodeFilename(), e);
    117             }
    118         }
    119     }
    120
    121     /***
    122      * Get the SourceType of the source file with given name. This depends on the fileName
    123      * extension, and the java version.
    124      * <p/>
    125      * For compatibility with older code that does not always pass in a correct filename,
    126      * unrecognized files are assumed to be java files.
    127      *
    128      * @param fileName Name of the file, can be absolute, or simple.
    129      * @return the SourceType
    130      */
    131     private SourceType getSourceTypeOfFile(String fileName) {
    132         SourceType sourceType = sourceTypeDiscoverer.getSourceTypeOfFile(fileName);
    133         if (sourceType == null) {
    134             // For compatibility with older code that does not always pass in
    135             // a correct filename.
    136             sourceType = sourceTypeDiscoverer.getSourceTypeOfJavaFiles();
    137         }
    138         return sourceType;
    139     }
    140
    141     /***
    142      * Processes the file read by the reader agains the rule set.
    143      *
    144      * @param reader  input stream reader
    145      * @param ruleSet set of rules to process against the file
    146      * @param ctx     context in which PMD is operating. This contains the Renderer and
    147      *                whatnot
    148      * @throws PMDException if the input could not be parsed or processed
    149      */
    150     public void processFile(Reader reader, RuleSet ruleSet, RuleContext ctx)
    151             throws PMDException {
    152         processFile(reader, new RuleSets(ruleSet), ctx);
    153     }
    154
    155     /***
    156      * Processes the input stream agains a rule set using the given input encoding.
    157      *
    158      * @param fileContents an input stream to analyze
    159      * @param encoding     input stream's encoding
    160      * @param ruleSet      set of rules to process against the file
    161      * @param ctx          context in which PMD is operating. This contains the Report and whatnot
    162      * @throws PMDException if the input encoding is unsupported or the input stream could
    163      *                      not be parsed
    164      * @see #processFile(Reader, RuleSet, RuleContext)
    165      */
    166     public void processFile(InputStream fileContents, String encoding,
    167                             RuleSet ruleSet, RuleContext ctx) throws PMDException {
    168         try {
    169             processFile(new InputStreamReader(fileContents, encoding), ruleSet, ctx);
    170         } catch (UnsupportedEncodingException uee) {
    171             throw new PMDException("Unsupported encoding exception: "
    172                     + uee.getMessage());
    173         }
    174     }
    175
    176     /***
    177      * Processes the input stream agains a rule set using the given input encoding.
    178      *
    179      * @param fileContents an input stream to analyze
    180      * @param encoding     input stream's encoding
    181      * @param ruleSets     set of rules to process against the file
    182      * @param ctx          context in which PMD is operating. This contains the Report and whatnot
    183      * @throws PMDException if the input encoding is unsupported or the input stream could
    184      *                      not be parsed
    185      * @see #processFile(Reader, RuleSet, RuleContext)
    186      */
    187     public void processFile(InputStream fileContents, String encoding,
    188                             RuleSets ruleSets, RuleContext ctx) throws PMDException {
    189         try {
    190             processFile(new InputStreamReader(fileContents, encoding), ruleSets, ctx);
    191         } catch (UnsupportedEncodingException uee) {
    192             throw new PMDException("Unsupported encoding exception: "
    193                     + uee.getMessage());
    194         }
    195     }
    196
    197     /***
    198      * Processes the input stream against a rule set assuming the platform character set.
    199      *
    200      * @param fileContents input stream to check
    201      * @param ruleSet      the set of rules to process against the source code
    202      * @param ctx          the context in which PMD is operating. This contains the Report and
    203      *                     whatnot
    204      * @throws PMDException if the input encoding is unsupported or the input input stream
    205      *                      could not be parsed
    206      * @see #processFile(InputStream, String, RuleSet, RuleContext)
    207      */
    208     public void processFile(InputStream fileContents, RuleSet ruleSet,
    209                             RuleContext ctx) throws PMDException {
    210         processFile(fileContents, System.getProperty("file.encoding"), ruleSet, ctx);
    211     }
    212
    213     public void setExcludeMarker(String marker) {
    214         this.excludeMarker = marker;
    215     }
    216
    217     /***
    218      * Set the SourceType to be used for ".java" files.
    219      *
    220      * @param javaVersion the SourceType that indicates the java version
    221      */
    222     public void setJavaVersion(SourceType javaVersion) {
    223         sourceTypeDiscoverer.setSourceTypeOfJavaFiles(javaVersion);
    224     }
    225
    226     public static void main(String[] args) {
    227         CommandLineOptions opts = new CommandLineOptions(args);
    228
    229         SourceFileSelector fileSelector = new SourceFileSelector();
    230
    231         fileSelector.setSelectJavaFiles(opts.isCheckJavaFiles());
    232         fileSelector.setSelectJspFiles(opts.isCheckJspFiles());
    233
    234         List files;
    235         if (opts.containsCommaSeparatedFileList()) {
    236             files = collectFromCommaDelimitedString(opts.getInputPath(),
    237                     fileSelector);
    238         } else {
    239             files = collectFilesFromOneName(opts.getInputPath(), fileSelector);
    240         }
    241
    242         PMD pmd = new PMD();
    243         if (opts.getTargetJDK().equals("1.3")) {
    244             if (opts.debugEnabled())
    245                 System.out.println("In JDK 1.3 mode");
    246             pmd.setJavaVersion(SourceType.JAVA_13);
    247         } else if (opts.getTargetJDK().equals("1.5")) {
    248             if (opts.debugEnabled())
    249                 System.out.println("In JDK 1.5 mode");
    250             pmd.setJavaVersion(SourceType.JAVA_15);
    251         } else {
    252             if (opts.debugEnabled())
    253                 System.out.println("In JDK 1.4 mode");
    254             pmd.setJavaVersion(SourceType.JAVA_14);
    255         }
    256         pmd.setExcludeMarker(opts.getExcludeMarker());
    257
    258         RuleContext ctx = new RuleContext();
    259         Report report = new Report();
    260         ctx.setReport(report);
    261         report.start();
    262
    263         try {
    264             RuleSetFactory ruleSetFactory = new RuleSetFactory();
    265             RuleSets rulesets = ruleSetFactory.createRuleSets(opts.getRulesets());
    266             printRuleNamesInDebug(opts.debugEnabled(), rulesets);
    267
    268             pmd.processFiles(files, ctx, rulesets, opts.debugEnabled(), opts
    269                     .shortNamesEnabled(), opts.getInputPath(), opts.getEncoding());
    270         } catch (FileNotFoundException fnfe) {
    271             System.out.println(opts.usage());
    272             fnfe.printStackTrace();
    273         } catch (RuleSetNotFoundException rsnfe) {
    274             System.out.println(opts.usage());
    275             rsnfe.printStackTrace();
    276         } catch (IOException ioe) {
    277             System.out.println(opts.usage());
    278             ioe.printStackTrace();
    279         }
    280         report.end();
    281
    282         try {
    283             Renderer r = opts.createRenderer();
    284             System.out.println(r.render(ctx.getReport()));
    285         } catch (Exception e) {
    286             System.out.println(e.getMessage());
    287             System.out.println(opts.usage());
    288             if (opts.debugEnabled()) {
    289                 e.printStackTrace();
    290             }
    291         }
    292     }
    293
    294     /***
    295      * Run PMD on a list of files.
    296      *
    297      * @param files             the List of DataSource instances.
    298      * @param ctx               the context in which PMD is operating. This contains the Report and
    299      *                          whatnot
    300      * @param rulesets          the RuleSets
    301      * @param debugEnabled
    302      * @param shortNamesEnabled
    303      * @param inputPath
    304      * @param encoding
    305      * @throws IOException If one of the files could not be read
    306      */
    307     public void processFiles(List files, RuleContext ctx, RuleSets rulesets,
    308                              boolean debugEnabled, boolean shortNamesEnabled, String inputPath,
    309                              String encoding) throws IOException {
    310         for (Iterator i = files.iterator(); i.hasNext();) {
    311             DataSource dataSource = (DataSource) i.next();
    312
    313             String niceFileName = dataSource.getNiceFileName(shortNamesEnabled,
    314                     inputPath);
    315             ctx.setSourceCodeFilename(niceFileName);
    316             if (debugEnabled) {
    317                 System.out.println("Processing " + ctx.getSourceCodeFilename());
    318             }
    319
    320             try {
    321                 InputStream stream = new BufferedInputStream(dataSource
    322                         .getInputStream());
    323                 processFile(stream, encoding, rulesets, ctx);
    324             } catch (PMDException pmde) {
    325                 if (debugEnabled) {
    326                     pmde.getReason().printStackTrace();
    327                 }
    328                 ctx.getReport().addError(new Report.ProcessingError(pmde.getMessage(), niceFileName));
    329             }
    330         }
    331     }
    332
    333     /***
    334      * If in debug modus, print the names of the rules.
    335      *
    336      * @param debugEnabled the boolean indicating if debug is enabled
    337      * @param rulesets     the RuleSets to print
    338      */
    339     private static void printRuleNamesInDebug(boolean debugEnabled, RuleSets rulesets) {
    340         if (debugEnabled) {
    341             for (Iterator i = rulesets.getAllRules().iterator(); i.hasNext();) {
    342                 Rule r = (Rule) i.next();
    343                 System.out.println("Loaded rule " + r.getName());
    344             }
    345         }
    346     }
    347
    348     /***
    349      * Collects the given file into a list.
    350      *
    351      * @param inputFileName a file name
    352      * @param fileSelector  Filtering of wanted source files
    353      * @return the list of files collected from the <code>inputFileName</code>
    354      * @see #collect(String)
    355      */
    356     private static List collectFilesFromOneName(String inputFileName,
    357                                                 SourceFileSelector fileSelector) {
    358         return collect(inputFileName, fileSelector);
    359     }
    360
    361     /***
    362      * Collects the files from the given comma-separated list.
    363      *
    364      * @param fileList     comma-separated list of filenames
    365      * @param fileSelector Filtering of wanted source files
    366      * @return list of files collected from the <code>fileList</code>
    367      */
    368     private static List collectFromCommaDelimitedString(String fileList,
    369                                                         SourceFileSelector fileSelector) {
    370         List files = new ArrayList();
    371         for (StringTokenizer st = new StringTokenizer(fileList, ","); st
    372                 .hasMoreTokens();) {
    373             files.addAll(collect(st.nextToken(), fileSelector));
    374         }
    375         return files;
    376     }
    377
    378     /***
    379      * Collects the files from the given <code>filename</code>.
    380      *
    381      * @param filename     the source from which to collect files
    382      * @param fileSelector Filtering of wanted source files
    383      * @return a list of files found at the given <code>filename</code>
    384      * @throws RuntimeException if <code>filename</code> is not found
    385      */
    386     private static List collect(String filename, SourceFileSelector fileSelector) {
    387         File inputFile = new File(filename);
    388         if (!inputFile.exists()) {
    389             throw new RuntimeException("File " + inputFile.getName()
    390                     + " doesn't exist");
    391         }
    392         List dataSources = new ArrayList();
    393         if (!inputFile.isDirectory()) {
    394             if (filename.endsWith(".zip") || filename.endsWith(".jar")) {
    395                 ZipFile zipFile;
    396                 try {
    397                     zipFile = new ZipFile(inputFile);
    398                     Enumeration e = zipFile.entries();
    399                     while (e.hasMoreElements()) {
    400                         ZipEntry zipEntry = (ZipEntry) e.nextElement();
    401                         if (fileSelector.isWantedFile(zipEntry.getName())) {
    402                             dataSources.add(new ZipDataSource(zipFile, zipEntry));
    403                         }
    404                     }
    405                 } catch (IOException ze) {
    406                     throw new RuntimeException("Zip file " + inputFile.getName()
    407                             + " can't be opened");
    408                 }
    409             } else {
    410                 dataSources.add(new FileDataSource(inputFile));
    411             }
    412         } else {
    413             FileFinder finder = new FileFinder();
    414             List files = finder.findFilesFrom(inputFile.getAbsolutePath(),
    415                     new SourceFileOrDirectoryFilter(fileSelector), true);
    416             for (Iterator i = files.iterator(); i.hasNext();) {
    417                 dataSources.add(new FileDataSource((File) i.next()));
    418             }
    419         }
    420         return dataSources;
    421     }
    422
    423 }