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(file) : true;
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 (!(o instanceof RuleSet)) {
222 return false; // Trivial
223 }
224
225 if (this == o) {
226 return true; // Basic equality
227 }
228
229 RuleSet ruleSet = (RuleSet) o;
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 }
|