RuleSet.java
001 /**
002  * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003  */
004 package net.sourceforge.pmd;
005 
006 import java.io.File;
007 import java.util.ArrayList;
008 import java.util.Collection;
009 import java.util.Iterator;
010 import java.util.List;
011 
012 import net.sourceforge.pmd.lang.Language;
013 import net.sourceforge.pmd.lang.LanguageVersion;
014 import net.sourceforge.pmd.lang.ast.Node;
015 import net.sourceforge.pmd.lang.rule.RuleReference;
016 import net.sourceforge.pmd.util.Benchmark;
017 import net.sourceforge.pmd.util.filter.Filter;
018 import net.sourceforge.pmd.util.filter.Filters;
019 
020 /**
021  * This class represents a collection of rules.
022  *
023  @see Rule
024  */
025 //FUTURE Implement Cloneable and clone()
026 public class RuleSet {
027 
028     private List<Rule> rules = new ArrayList<Rule>();
029     private String fileName;
030     private String name = "";
031     private String description = "";
032     private List<String> excludePatterns = new ArrayList<String>(0);
033     private List<String> includePatterns = new ArrayList<String>(0);
034     private Filter<File> filter;
035 
036     /**
037      * Returns the number of rules in this ruleset
038      *
039      @return an int representing the number of rules
040      */
041     public int size() {
042   return rules.size();
043     }
044 
045     /**
046      * Add a new rule to this ruleset
047      *
048      @param rule the rule to be added
049      */
050     public void addRule(Rule rule) {
051   if (rule == null) {
052       throw new RuntimeException("Null Rule reference added to a RuleSet; that's a bug somewhere in PMD");
053   }
054   rules.add(rule);
055     }
056 
057     /**
058      * Add a new rule by reference to this ruleset.
059      *
060      @param ruleSetFileName the ruleset which contains the rule
061      @param rule the rule to be added
062      */
063     public void addRuleByReference(String ruleSetFileName, Rule rule) {
064   if (ruleSetFileName == null) {
065       throw new RuntimeException("Adding a rule by reference is not allowed with a null rule set file name.");
066   }
067   if (rule == null) {
068       throw new RuntimeException("Null Rule reference added to a RuleSet; that's a bug somewhere in PMD");
069   }
070   if (!(rule instanceof RuleReference)) {
071       RuleSetReference ruleSetReference = new RuleSetReference();
072       ruleSetReference.setRuleSetFileName(ruleSetFileName);
073       RuleReference ruleReference = new RuleReference();
074       ruleReference.setRule(rule);
075       ruleReference.setRuleSetReference(ruleSetReference);
076       rule = ruleReference;
077   }
078   rules.add(rule);
079     }
080 
081     /**
082      * Returns the actual Collection of rules in this ruleset
083      *
084      @return a Collection with the rules. All objects are of type {@link Rule}
085      */
086     public Collection<Rule> getRules() {
087   return rules;
088     }
089 
090     /**
091      * Does any Rule for the given Language use the DFA layer?
092      @param language The Language.
093      @return <code>true</code> if a Rule for the Language uses the DFA layer,
094      <code>false</code> otherwise.
095      */
096     public boolean usesDFA(Language language) {
097   for (Rule r : rules) {
098       if (r.getLanguage().equals(language)) {
099     if (r.usesDFA()) {
100         return true;
101     }
102       }
103   }
104   return false;
105     }
106 
107     /**
108      * Returns the Rule with the given name
109      *
110      @param ruleName the name of the rule to find
111      @return the rule or null if not found
112      */
113     public Rule getRuleByName(String ruleName) {
114   Rule rule = null;
115   for (Iterator<Rule> i = rules.iterator(); i.hasNext() && rule == null;) {
116       Rule r = i.next();
117       if (r.getName().equals(ruleName)) {
118     rule = r;
119       }
120   }
121   return rule;
122     }
123 
124     /**
125      * Add a whole RuleSet to this RuleSet
126      *
127      @param ruleSet the RuleSet to add
128      */
129     public void addRuleSet(RuleSet ruleSet) {
130   rules.addAll(rules.size(), ruleSet.getRules());
131     }
132 
133     /**
134      * Add all rules by reference from one RuleSet to this RuleSet.  The rules
135      * can be added as individual references, or collectively as an all rule
136      * reference.
137      *
138      @param ruleSet the RuleSet to add
139      @param allRules 
140      */
141     public void addRuleSetByReference(RuleSet ruleSet, boolean allRules) {
142   if (ruleSet.getFileName() == null) {
143       throw new RuntimeException("Adding a rule by reference is not allowed with a null rule set file name.");
144   }
145   RuleSetReference ruleSetReference = new RuleSetReference();
146   ruleSetReference.setRuleSetFileName(ruleSet.getFileName());
147   ruleSetReference.setAllRules(allRules);
148   for (Rule rule : ruleSet.getRules()) {
149       RuleReference ruleReference = new RuleReference();
150       ruleReference.setRule(rule);
151       ruleReference.setRuleSetReference(ruleSetReference);
152       rules.add(ruleReference);
153   }
154     }
155 
156     /**
157      * Check if a given source file should be checked by rules in this RuleSet.  A file
158      * should not be checked if there is an <code>exclude</code> pattern which matches
159      * the file, unless there is an <code>include</code> pattern which also matches
160      * the file.  In other words, <code>include</code> patterns override <code>exclude</code>
161      * patterns.
162      *
163      @param file the source file to check
164      @return <code>true</code> if the file should be checked, <code>false</code> otherwise
165      */
166     public boolean applies(File file) {
167   // Initialize filter based on patterns
168   if (filter == null) {
169       Filter<String> regexFilter = Filters.buildRegexFilterIncludeOverExclude(includePatterns, excludePatterns);
170       filter = Filters.toNormalizedFileFilter(regexFilter);
171   }
172 
173   return file != null ? filter.filter(filetrue;
174     }
175 
176     public void start(RuleContext ctx) {
177   for (Rule rule : rules) {
178       rule.start(ctx);
179   }
180     }
181 
182     public void apply(List<? extends Node> acuList, RuleContext ctx) {
183   long start = System.nanoTime();
184   for (Rule rule : rules) {
185       if (!rule.usesRuleChain() && applies(rule, ctx.getLanguageVersion())) {
186     rule.apply(acuList, ctx);
187     long end = System.nanoTime();
188     Benchmark.mark(Benchmark.TYPE_RULE, rule.getName(), end - start, 1);
189     start = end;
190       }
191   }
192     }
193 
194     /**
195      * Does the given Rule apply to the given LanguageVersion?  If so, the
196      * Language must be the same and be between the minimum and maximums
197      * versions on the Rule.
198      
199      @param rule The rule.
200      @param languageVersion The language version.
201      */
202     public static boolean applies(Rule rule, LanguageVersion languageVersion) {
203   final LanguageVersion min = rule.getMinimumLanguageVersion();
204   final LanguageVersion max = rule.getMinimumLanguageVersion();
205   return rule.getLanguage().equals(languageVersion.getLanguage())
206     && (min == null || min.compareTo(languageVersion<= 0)
207     && (max == null || max.compareTo(languageVersion>= 0);
208     }
209 
210     public void end(RuleContext ctx) {
211   for (Rule rule : rules) {
212       rule.end(ctx);
213   }
214     }
215 
216     /**
217      @see java.lang.Object#equals(java.lang.Object)
218      */
219     @Override
220     public boolean equals(Object o) {
221   if (!(instanceof RuleSet)) {
222       return false// Trivial
223   }
224 
225   if (this == o) {
226       return true// Basic equality
227   }
228 
229   RuleSet ruleSet = (RuleSeto;
230   return this.getName().equals(ruleSet.getName()) && this.getRules().equals(ruleSet.getRules());
231     }
232 
233     /**
234      @see java.lang.Object#hashCode()
235      */
236     @Override
237     public int hashCode() {
238   return this.getName().hashCode() 13 this.getRules().hashCode();
239     }
240 
241     public String getFileName() {
242   return fileName;
243     }
244 
245     public void setFileName(String fileName) {
246   this.fileName = fileName;
247     }
248 
249     public String getName() {
250   return name;
251     }
252 
253     public void setName(String name) {
254   this.name = name;
255     }
256 
257     public String getDescription() {
258   return description;
259     }
260 
261     public void setDescription(String description) {
262   this.description = description;
263     }
264 
265     public List<String> getExcludePatterns() {
266   return this.excludePatterns;
267     }
268 
269     public void addExcludePattern(String excludePattern) {
270   this.excludePatterns.add(excludePattern);
271     }
272 
273     public void addExcludePatterns(List<String> excludePatterns) {
274   this.excludePatterns.addAll(excludePatterns);
275     }
276 
277     public void setExcludePatterns(List<String> excludePatterns) {
278   this.excludePatterns = excludePatterns;
279     }
280 
281     public List<String> getIncludePatterns() {
282   return this.includePatterns;
283     }
284 
285     public void addIncludePattern(String includePattern) {
286   this.includePatterns.add(includePattern);
287     }
288 
289     public void addIncludePatterns(List<String> includePatterns) {
290   this.includePatterns.addAll(includePatterns);
291     }
292 
293     public void setIncludePatterns(List<String> includePatterns) {
294   this.includePatterns = includePatterns;
295     }
296 
297     /**
298      * Does any Rule for the given Language use Type Resolution?
299      @param language The Language.
300      @return <code>true</code> if a Rule for the Language uses Type Resolution,
301      <code>false</code> otherwise.
302      */
303     public boolean usesTypeResolution(Language language) {
304   for (Rule r : rules) {
305       if (r.getLanguage().equals(language)) {
306     if (r.usesTypeResolution()) {
307         return true;
308     }
309       }
310   }
311   return false;
312     }
313 
314 }