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