/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.util.Collections;
import java.util.List;
import org.apache.commons.io.IOUtils;
import net.sourceforge.pmd.benchmark.Benchmark;
import net.sourceforge.pmd.benchmark.Benchmarker;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.lang.LanguageVersionHandler;
import net.sourceforge.pmd.lang.Parser;
import net.sourceforge.pmd.lang.VisitorStarter;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.ast.ParseException;
import net.sourceforge.pmd.lang.xpath.Initializer;
public class SourceCodeProcessor {
private final PMDConfiguration configuration;
public SourceCodeProcessor(PMDConfiguration configuration) {
this.configuration = configuration;
}
/**
* Processes the input stream against a rule set using the given input
* encoding.
*
* @param sourceCode
* The InputStream to analyze.
* @param ruleSets
* The collection of rules to process against the file.
* @param ctx
* The context in which PMD is operating.
* @throws PMDException
* if the input encoding is unsupported, the input stream could
* not be parsed, or other error is encountered.
* @see #processSourceCode(Reader, RuleSets, RuleContext)
*/
public void processSourceCode(InputStream sourceCode, RuleSets ruleSets, RuleContext ctx) throws PMDException {
try {
processSourceCode(new InputStreamReader(sourceCode, configuration.getSourceEncoding()), ruleSets, ctx);
} catch (UnsupportedEncodingException uee) {
throw new PMDException("Unsupported encoding exception: " + uee.getMessage());
}
}
/**
* Processes the input stream against a rule set using the given input
* encoding. If the LanguageVersion is <code>null</code> on the RuleContext,
* it will be automatically determined. Any code which wishes to process
* files for different Languages, will need to be sure to either properly
* set the Language on the RuleContext, or set it to <code>null</code>
* first.
*
* @see RuleContext#setLanguageVersion(net.sourceforge.pmd.lang.LanguageVersion)
* @see PMDConfiguration#getLanguageVersionOfFile(String)
*
* @param sourceCode
* The Reader to analyze.
* @param ruleSets
* The collection of rules to process against the file.
* @param ctx
* The context in which PMD is operating.
* @throws PMDException
* if the input encoding is unsupported, the input stream could
* not be parsed, or other error is encountered.
*/
public void processSourceCode(Reader sourceCode, RuleSets ruleSets, RuleContext ctx) throws PMDException {
determineLanguage(ctx);
// make sure custom XPath functions are initialized
Initializer.initialize();
// Coarse check to see if any RuleSet applies to file, will need to do a finer RuleSet specific check later
if (ruleSets.applies(ctx.getSourceCodeFile())) {
// Is the cache up to date?
if (configuration.getAnalysisCache().isUpToDate(ctx.getSourceCodeFile())) {
for (final RuleViolation rv : configuration.getAnalysisCache().getCachedViolations(ctx.getSourceCodeFile())) {
ctx.getReport().addRuleViolation(rv);
}
return;
}
try {
ruleSets.start(ctx);
processSource(sourceCode, ruleSets, ctx);
} catch (ParseException pe) {
configuration.getAnalysisCache().analysisFailed(ctx.getSourceCodeFile());
throw new PMDException("Error while parsing " + ctx.getSourceCodeFilename(), pe);
} catch (Exception e) {
configuration.getAnalysisCache().analysisFailed(ctx.getSourceCodeFile());
throw new PMDException("Error while processing " + ctx.getSourceCodeFilename(), e);
} finally {
IOUtils.closeQuietly(sourceCode);
ruleSets.end(ctx);
}
}
}
private Node parse(RuleContext ctx, Reader sourceCode, Parser parser) {
long start = System.nanoTime();
Node rootNode = parser.parse(ctx.getSourceCodeFilename(), sourceCode);
ctx.getReport().suppress(parser.getSuppressMap());
long end = System.nanoTime();
Benchmarker.mark(Benchmark.Parser, end - start, 0);
return rootNode;
}
private void symbolFacade(Node rootNode, LanguageVersionHandler languageVersionHandler) {
long start = System.nanoTime();
languageVersionHandler.getSymbolFacade(configuration.getClassLoader()).start(rootNode);
long end = System.nanoTime();
Benchmarker.mark(Benchmark.SymbolTable, end - start, 0);
}
// private ParserOptions getParserOptions(final LanguageVersionHandler
// languageVersionHandler) {
// // TODO Handle Rules having different parser options.
// ParserOptions parserOptions =
// languageVersionHandler.getDefaultParserOptions();
// parserOptions.setSuppressMarker(configuration.getSuppressMarker());
// return parserOptions;
// }
private void usesDFA(LanguageVersion languageVersion, Node rootNode, RuleSets ruleSets, Language language) {
if (ruleSets.usesDFA(language)) {
long start = System.nanoTime();
VisitorStarter dataFlowFacade = languageVersion.getLanguageVersionHandler().getDataFlowFacade();
dataFlowFacade.start(rootNode);
long end = System.nanoTime();
Benchmarker.mark(Benchmark.DFA, end - start, 0);
}
}
private void usesTypeResolution(LanguageVersion languageVersion, Node rootNode, RuleSets ruleSets,
Language language) {
if (ruleSets.usesTypeResolution(language)) {
long start = System.nanoTime();
languageVersion.getLanguageVersionHandler().getTypeResolutionFacade(configuration.getClassLoader())
.start(rootNode);
long end = System.nanoTime();
Benchmarker.mark(Benchmark.TypeResolution, end - start, 0);
}
}
private void processSource(Reader sourceCode, RuleSets ruleSets, RuleContext ctx) {
LanguageVersion languageVersion = ctx.getLanguageVersion();
LanguageVersionHandler languageVersionHandler = languageVersion.getLanguageVersionHandler();
Parser parser = PMD.parserFor(languageVersion, configuration);
Node rootNode = parse(ctx, sourceCode, parser);
symbolFacade(rootNode, languageVersionHandler);
Language language = languageVersion.getLanguage();
usesDFA(languageVersion, rootNode, ruleSets, language);
usesTypeResolution(languageVersion, rootNode, ruleSets, language);
List<Node> acus = Collections.singletonList(rootNode);
ruleSets.apply(acus, ctx, language);
}
private void determineLanguage(RuleContext ctx) {
// If LanguageVersion of the source file is not known, make a
// determination
if (ctx.getLanguageVersion() == null) {
LanguageVersion languageVersion = configuration.getLanguageVersionOfFile(ctx.getSourceCodeFilename());
ctx.setLanguageVersion(languageVersion);
}
}
}