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 java.io.InputStream;
7   import java.io.InputStreamReader;
8   import java.io.Reader;
9   import java.io.UnsupportedEncodingException;
10  import java.util.ArrayList;
11  import java.util.List;
12  
13  import net.sourceforge.pmd.benchmark.Benchmark;
14  import net.sourceforge.pmd.benchmark.Benchmarker;
15  import net.sourceforge.pmd.lang.*;
16  import net.sourceforge.pmd.lang.ast.Node;
17  import net.sourceforge.pmd.lang.ast.ParseException;
18  import net.sourceforge.pmd.lang.xpath.Initializer;
19  
20  import org.apache.commons.io.IOUtils;
21  
22  public class SourceCodeProcessor {
23  
24      private final PMDConfiguration configuration;
25  
26      public SourceCodeProcessor(PMDConfiguration configuration) {
27      	this.configuration = configuration;
28      }
29      
30      
31      /**
32       * Processes the input stream against a rule set using the given input encoding.
33       *
34       * @param sourceCode The InputStream to analyze.
35       * @param ruleSets The collection of rules to process against the file.
36       * @param ctx The context in which PMD is operating.
37       * @throws PMDException if the input encoding is unsupported, the input stream could
38       *                      not be parsed, or other error is encountered.
39       * @see #processSourceCode(Reader, RuleSets, RuleContext)
40       */
41      public void processSourceCode(InputStream sourceCode, RuleSets ruleSets, RuleContext ctx) throws PMDException {
42  		try {
43  		    processSourceCode(new InputStreamReader(sourceCode, configuration.getSourceEncoding()), ruleSets, ctx);
44  		} catch (UnsupportedEncodingException uee) {
45  		    throw new PMDException("Unsupported encoding exception: " + uee.getMessage());
46  		}
47      }
48      
49      
50      /**
51       * Processes the input stream against a rule set using the given input encoding.
52       * If the LanguageVersion is <code>null</code>  on the RuleContext, it will
53       * be automatically determined.  Any code which wishes to process files for
54       * different Languages, will need to be sure to either properly set the
55       * Language on the RuleContext, or set it to <code>null</code> first.
56       *
57       * @see RuleContext#setLanguageVersion(net.sourceforge.pmd.lang.LanguageVersion)
58       * @see PMDConfiguration#getLanguageVersionOfFile(String)
59       *
60       * @param sourceCode The Reader to analyze.
61       * @param ruleSets The collection of rules to process against the file.
62       * @param ctx The context in which PMD is operating.
63       * @throws PMDException if the input encoding is unsupported, the input stream could
64       *                      not be parsed, or other error is encountered.
65       */
66      public void processSourceCode(Reader sourceCode, RuleSets ruleSets, RuleContext ctx) throws PMDException {
67      	determineLanguage(ctx);
68  
69  		// make sure custom XPath functions are initialized
70  		Initializer.initialize();
71  
72  	    // Coarse check to see if any RuleSet applies to file, will need to do a finer RuleSet specific check later
73  		 if (ruleSets.applies(ctx.getSourceCodeFile())) {
74  
75  		try {
76  			processSource(sourceCode, ruleSets,ctx);
77  
78  		} catch (ParseException pe) {
79  		    throw new PMDException("Error while parsing " + ctx.getSourceCodeFilename(), pe);
80  		} catch (Exception e) {
81  		    throw new PMDException("Error while processing " + ctx.getSourceCodeFilename(), e);
82  		} finally {
83  		    IOUtils.closeQuietly(sourceCode);
84  		}
85  		}
86      }
87  
88      
89      private Node parse(RuleContext ctx, Reader sourceCode, Parser parser) {
90  		long start = System.nanoTime();
91  		Node rootNode = parser.parse(ctx.getSourceCodeFilename(), sourceCode);
92  		ctx.getReport().suppress(parser.getSuppressMap());
93  		long end = System.nanoTime();    	
94  		Benchmarker.mark(Benchmark.Parser, end - start, 0);
95  		return rootNode;
96      }
97  
98      private void symbolFacade(Node rootNode, LanguageVersionHandler languageVersionHandler) {
99      	long start = System.nanoTime();
100 		languageVersionHandler.getSymbolFacade(configuration.getClassLoader()).start(rootNode);
101 		long end = System.nanoTime();
102 		Benchmarker.mark(Benchmark.SymbolTable, end - start, 0);
103     }
104     
105 //    private ParserOptions getParserOptions(final LanguageVersionHandler languageVersionHandler) {
106 //		// TODO Handle Rules having different parser options.
107 //		ParserOptions parserOptions = languageVersionHandler.getDefaultParserOptions();
108 //		parserOptions.setSuppressMarker(configuration.getSuppressMarker());
109 //		return parserOptions;
110 //    }
111 
112     private void usesDFA(LanguageVersion languageVersion, Node rootNode, RuleSets ruleSets, Language language ) {
113 
114 		if (ruleSets.usesDFA(language)) {
115 		    long start = System.nanoTime();
116 		    VisitorStarter dataFlowFacade = languageVersion.getLanguageVersionHandler().getDataFlowFacade();
117 		    dataFlowFacade.start(rootNode);
118 		    long end = System.nanoTime();
119 		    Benchmarker.mark(Benchmark.DFA, end - start, 0);
120 		}
121     }
122 
123     private void usesTypeResolution(LanguageVersion languageVersion, Node rootNode, RuleSets ruleSets, Language language) {
124 	
125 		if (ruleSets.usesTypeResolution(language)) {
126 		    long start = System.nanoTime();
127 		    languageVersion.getLanguageVersionHandler().getTypeResolutionFacade(configuration.getClassLoader()).start(rootNode);
128 		    long end = System.nanoTime();
129 		    Benchmarker.mark(Benchmark.TypeResolution, end - start, 0);
130 		}
131     }
132     
133     private void processSource(Reader sourceCode, RuleSets ruleSets, RuleContext ctx) {
134 		LanguageVersion languageVersion = ctx.getLanguageVersion();
135 		LanguageVersionHandler languageVersionHandler = languageVersion.getLanguageVersionHandler();
136 		Parser parser = PMD.parserFor(languageVersion, configuration);
137 		
138 		Node rootNode = parse(ctx, sourceCode, parser);
139 		symbolFacade(rootNode, languageVersionHandler);
140 		Language language = languageVersion.getLanguage();
141 		usesDFA(languageVersion, rootNode, ruleSets, language);
142 		usesTypeResolution(languageVersion, rootNode, ruleSets,language);
143 		
144 		List<Node> acus = new ArrayList<>();
145 		acus.add(rootNode);
146 		ruleSets.apply(acus, ctx, language);
147 	}
148 
149 
150 
151 	private void determineLanguage(RuleContext ctx) {
152 		// If LanguageVersion of the source file is not known, make a determination
153 		if (ctx.getLanguageVersion() == null) {
154 		    LanguageVersion languageVersion = configuration.getLanguageVersionOfFile(ctx.getSourceCodeFilename());
155 		    ctx.setLanguageVersion(languageVersion);
156 		}
157     }
158 }