View Javadoc
1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.lang.rule;
5   
6   import java.util.ArrayList;
7   import java.util.List;
8   
9   import net.sourceforge.pmd.AbstractPropertySource;
10  import net.sourceforge.pmd.Rule;
11  import net.sourceforge.pmd.RuleContext;
12  import net.sourceforge.pmd.RulePriority;
13  import net.sourceforge.pmd.lang.Language;
14  import net.sourceforge.pmd.lang.LanguageVersion;
15  import net.sourceforge.pmd.lang.ParserOptions;
16  import net.sourceforge.pmd.lang.ast.Node;
17  
18  /**
19   * Basic abstract implementation of all parser-independent methods of the Rule
20   * interface.
21   *
22   * @author pieter_van_raemdonck - Application Engineers NV/SA - www.ae.be
23   */
24  // FUTURE Implement Cloneable and clone()?
25  public abstract class AbstractRule extends AbstractPropertySource implements Rule {
26  
27  	private Language language;
28  	private LanguageVersion minimumLanguageVersion;
29  	private LanguageVersion maximumLanguageVersion;
30  	private boolean deprecated;
31  	private String name = getClass().getName();
32  	private String since;
33  	private String ruleClass = getClass().getName();
34  	private String ruleSetName;
35  	private String message;
36  	private String description;
37  	private List<String> examples = new ArrayList<>();
38  	private String externalInfoUrl;
39  	private RulePriority priority = RulePriority.LOW;
40  	private boolean usesDFA;
41  	private boolean usesTypeResolution;
42  	private List<String> ruleChainVisits = new ArrayList<>();
43  
44  	public AbstractRule() {
45  		definePropertyDescriptor(Rule.VIOLATION_SUPPRESS_REGEX_DESCRIPTOR);
46  		definePropertyDescriptor(Rule.VIOLATION_SUPPRESS_XPATH_DESCRIPTOR);
47  	}
48  
49  	public void deepCopyValuesTo(AbstractRule otherRule) {
50  		otherRule.language = language;
51  		otherRule.minimumLanguageVersion = minimumLanguageVersion;
52  		otherRule.maximumLanguageVersion = maximumLanguageVersion;
53  		otherRule.deprecated = deprecated;
54  		otherRule.name = name;
55  		otherRule.since = since;
56  		otherRule.ruleClass = ruleClass;
57  		otherRule.ruleSetName = ruleSetName;
58  		otherRule.message = message;
59  		otherRule.description = description;
60  		otherRule.examples = copyExamples();
61  		otherRule.externalInfoUrl = externalInfoUrl;
62  		otherRule.priority = priority;
63  		otherRule.propertyDescriptors = copyPropertyDescriptors();
64  		otherRule.propertyValuesByDescriptor = copyPropertyValues();
65  		otherRule.usesDFA = usesDFA;
66  		otherRule.usesTypeResolution = usesTypeResolution;
67  		otherRule.ruleChainVisits = copyRuleChainVisits();
68  	}
69  
70  	private List<String> copyExamples() {
71  		List<String> copy = new ArrayList<>(examples.size());
72  		copy.addAll(examples);
73  		return copy;    	
74  	}
75  
76  	private List<String> copyRuleChainVisits() {
77  		List<String> copy = new ArrayList<>(ruleChainVisits.size());
78  		copy.addAll(ruleChainVisits);
79  		return copy;
80  	}
81  
82  	/**
83  	 * @see Rule#getLanguage()
84  	 */
85  	 public Language getLanguage() {
86  		return language;
87  	}
88  
89  	/**
90  	 * @see Rule#setLanguage(net.sourceforge.pmd.lang.Language)
91  	 */
92  	 public void setLanguage(Language language) {
93  		 if (this.language != null && this instanceof ImmutableLanguage && !this.language.equals(language)) {
94  			 throw new UnsupportedOperationException("The Language for Rule class " + this.getClass().getName()
95  					 + " is immutable and cannot be changed.");
96  		 }
97  		 this.language = language;
98  	 }
99  
100 	 /**
101 	  * @see Rule#getMinimumLanguageVersion()
102 	  */
103 	 public LanguageVersion getMinimumLanguageVersion() {
104 		 return minimumLanguageVersion;
105 	 }
106 
107 	 /**
108 	  * @see Rule#setMinimumLanguageVersion(net.sourceforge.pmd.lang.LanguageVersion)
109 	  */
110 	 public void setMinimumLanguageVersion(LanguageVersion minimumLanguageVersion) {
111 		 this.minimumLanguageVersion = minimumLanguageVersion;
112 	 }
113 
114 	 /**
115 	  * @see Rule#getMaximumLanguageVersion()
116 	  */
117 	 public LanguageVersion getMaximumLanguageVersion() {
118 		 return maximumLanguageVersion;
119 	 }
120 
121 	 /**
122 	  * @see Rule#setMaximumLanguageVersion(net.sourceforge.pmd.lang.LanguageVersion)
123 	  */
124 	 public void setMaximumLanguageVersion(LanguageVersion maximumLanguageVersion) {
125 		 this.maximumLanguageVersion = maximumLanguageVersion;
126 	 }
127 
128 	 /**
129 	  * @see Rule#isDeprecated()
130 	  */
131 	 public boolean isDeprecated() {
132 		 return deprecated;
133 	 }
134  
135 	 /**
136 	  * @see Rule#setDeprecated(boolean)
137 	  */
138 	 public void setDeprecated(boolean deprecated) {
139 		 this.deprecated = deprecated;
140 	 }
141 
142 	 /**
143 	  * @see Rule#getName()
144 	  */
145 	 public String getName() {
146 		 return name;
147 	 }
148 
149 	 /**
150 	  * @see Rule#setName(String)
151 	  */
152 	 public void setName(String name) {
153 		 this.name = name;
154 	 }
155 
156 	 /**
157 	  * @see Rule#getSince()
158 	  */
159 	 public String getSince() {
160 		 return since;
161 	 }
162 
163 	 /**
164 	  * @see Rule#setSince(String)
165 	  */
166 	 public void setSince(String since) {
167 		 this.since = since;
168 	 }
169 
170 	 /**
171 	  * @see Rule#getRuleClass()
172 	  */
173 	 public String getRuleClass() {
174 		 return ruleClass;
175 	 }
176 
177 	 /**
178 	  * @see Rule#setRuleClass(String)
179 	  */
180 	 public void setRuleClass(String ruleClass) {
181 		 this.ruleClass = ruleClass;
182 	 }
183 
184 	 /**
185 	  * @see Rule#getRuleSetName()
186 	  */
187 	 public String getRuleSetName() {
188 		 return ruleSetName;
189 	 }
190 
191 	 /**
192 	  * @see Rule#setRuleSetName(String)
193 	  */
194 	 public void setRuleSetName(String ruleSetName) {
195 		 this.ruleSetName = ruleSetName;
196 	 }
197 
198 	 /**
199 	  * @see Rule#getMessage()
200 	  */
201 	 public String getMessage() {
202 		 return message;
203 	 }
204 
205 	 /**
206 	  * @see Rule#setMessage(String)
207 	  */
208 	 public void setMessage(String message) {
209 		 this.message = message;
210 	 }
211 
212 	 /**
213 	  * @see Rule#getDescription()
214 	  */
215 	 public String getDescription() {
216 		 return description;
217 	 }
218 
219 	 /**
220 	  * @see Rule#setDescription(String)
221 	  */
222 	 public void setDescription(String description) {
223 		 this.description = description;
224 	 }
225 
226 	 /**
227 	  * @see Rule#getExamples()
228 	  */
229 	 public List<String> getExamples() {
230 		 // TODO Needs to be externally immutable
231 		 return examples;
232 	 }
233 
234 	 /**
235 	  * @see Rule#addExample(String)
236 	  */
237 	 public void addExample(String example) {
238 		 examples.add(example);
239 	 }
240 
241 	 /**
242 	  * @see Rule#getExternalInfoUrl()
243 	  */
244 	 public String getExternalInfoUrl() {
245 		 return externalInfoUrl;
246 	 }
247 
248 	 /**
249 	  * @see Rule#setExternalInfoUrl(String)
250 	  */
251 	 public void setExternalInfoUrl(String externalInfoUrl) {
252 		 this.externalInfoUrl = externalInfoUrl;
253 	 }
254 
255 	 /**
256 	  * @see Rule#getPriority()
257 	  */
258 	 public RulePriority getPriority() {
259 		 return priority;
260 	 }
261 
262 	 /**
263 	  * @see Rule#setPriority(RulePriority)
264 	  */
265 	 public void setPriority(RulePriority priority) {
266 		 this.priority = priority;
267 	 }
268 
269 	 /**
270 	  * This implementation returns a new instance of {@link ParserOptions} using default settings.
271 	  * 
272 	  * @see Rule#setPriority(RulePriority)
273 	  */
274 	 public ParserOptions getParserOptions() {
275 		 return new ParserOptions();
276 	 }
277 
278 	 /**
279 	  * @see Rule#setUsesDFA()
280 	  */
281 	 public void setUsesDFA() {
282 		 usesDFA = true;
283 	 }
284 
285 	 /**
286 	  * @see Rule#usesDFA()
287 	  */
288 	 public boolean usesDFA() {
289 		 return usesDFA;
290 	 }
291 
292 	 /**
293 	  * @see Rule#setUsesTypeResolution()
294 	  */
295 	 public void setUsesTypeResolution() {
296 		 usesTypeResolution = true;
297 	 }
298 
299 	 /**
300 	  * @see Rule#usesTypeResolution()
301 	  */
302 	 public boolean usesTypeResolution() {
303 		 return usesTypeResolution;
304 	 }
305 
306 	 /**
307 	  * @see Rule#usesRuleChain()
308 	  */
309 	 public boolean usesRuleChain() {
310 		 return !getRuleChainVisits().isEmpty();
311 	 }
312 
313 	 /**
314 	  * @see Rule#getRuleChainVisits()
315 	  */
316 	 public List<String> getRuleChainVisits() {
317 		 return ruleChainVisits;
318 	 }
319 
320 	 /**
321 	  * @see Rule#addRuleChainVisit(Class)
322 	  */
323 	 public void addRuleChainVisit(Class<? extends Node> nodeClass) {
324 		 if (!nodeClass.getSimpleName().startsWith("AST")) {
325 			 throw new IllegalArgumentException("Node class does not start with 'AST' prefix: " + nodeClass);
326 		 }
327 		 addRuleChainVisit(nodeClass.getSimpleName().substring("AST".length()));
328 	 }
329 
330 	 /**
331 	  * @see Rule#addRuleChainVisit(String)
332 	  */
333 	 public void addRuleChainVisit(String astNodeName) {
334 		 if (!ruleChainVisits.contains(astNodeName)) {
335 			 ruleChainVisits.add(astNodeName);
336 		 }
337 	 }
338 
339 	 /**
340 	  * @see Rule#start(RuleContext)
341 	  */
342 	 public void start(RuleContext ctx) {
343 		 // Override as needed
344 	 }
345 
346 	 /**
347 	  * @see Rule#end(RuleContext)
348 	  */
349 	 public void end(RuleContext ctx) {
350 		 // Override as needed
351 	 }
352 
353 	 /**
354 	  * @see RuleViolationFactory#addViolation(RuleContext, Rule, Node, String, Object[])
355 	  */
356 	 public void addViolation(Object data, Node node) {
357 		 RuleContext ruleContext = (RuleContext) data;
358 		 ruleContext.getLanguageVersion().getLanguageVersionHandler().getRuleViolationFactory().addViolation(
359 				 ruleContext, this, node, this.getMessage(), null);
360 	 }
361 
362 	 /**
363 	  * @see RuleViolationFactory#addViolation(RuleContext, Rule, Node, String, Object[])
364 	  */
365 	 public void addViolation(Object data, Node node, String arg) {
366 		 RuleContext ruleContext = (RuleContext) data;
367 		 ruleContext.getLanguageVersion().getLanguageVersionHandler().getRuleViolationFactory().addViolation(
368 				 ruleContext, this, node, this.getMessage(), new Object[] { arg });
369 	 }
370 
371 	 /**
372 	  * @see RuleViolationFactory#addViolation(RuleContext, Rule, Node, String, Object[])
373 	  */
374 	 public void addViolation(Object data, Node node, Object[] args) {
375 		 RuleContext ruleContext = (RuleContext) data;
376 		 ruleContext.getLanguageVersion().getLanguageVersionHandler().getRuleViolationFactory().addViolation(
377 				 ruleContext, this, node, this.getMessage(), args);
378 	 }
379 
380 	 /**
381 	  * @see RuleViolationFactory#addViolation(RuleContext, Rule, Node, String, Object[])
382 	  */
383 	 public void addViolationWithMessage(Object data, Node node, String message) {
384 		 RuleContext ruleContext = (RuleContext) data;
385 		 ruleContext.getLanguageVersion().getLanguageVersionHandler().getRuleViolationFactory().addViolation(
386 				 ruleContext, this, node, message, null);
387 	 }
388 
389 	 /**
390 	  * @see RuleViolationFactory#addViolation(RuleContext, Rule, Node, String, Object[])
391 	  */
392 	 public void addViolationWithMessage(Object data, Node node, String message, int beginLine, int endLine) {
393 		 RuleContext ruleContext = (RuleContext) data;
394 		 ruleContext.getLanguageVersion().getLanguageVersionHandler().getRuleViolationFactory().addViolation(
395 				 ruleContext, this, node, message, beginLine, endLine, null 
396 				 );
397 	 }
398 	 
399 	 /**
400 	  * @see RuleViolationFactory#addViolation(RuleContext, Rule, Node, String, Object[])
401 	  */
402 	 public void addViolationWithMessage(Object data, Node node, String message, Object[] args) {
403 		 RuleContext ruleContext = (RuleContext) data;
404 		 ruleContext.getLanguageVersion().getLanguageVersionHandler().getRuleViolationFactory().addViolation(
405 				 ruleContext, this, node, message, args);
406 	 }
407 
408 	 /**
409 	  * Rules are equal if:
410 	  * <ol>
411 	  * <li>They have the same implementation class.</li>
412 	  * <li>They have the same name.</li>
413 	  * <li>They have the same priority.</li>
414 	  * <li>They share the same properties.</li>
415 	  * </ol>
416 	  */
417 	 @Override
418 	 public boolean equals(Object o) {
419 		 if (o == null) {
420 			 return false; // trivial
421 		 }
422 
423 		 if (this == o) {
424 			 return true; // trivial
425 		 }
426 
427 		 boolean equality = getClass() == o.getClass();
428 
429 		 if (equality) {
430 			 Rule that = (Rule) o;
431 			 equality = getName().equals(that.getName()) && getPriority().equals(that.getPriority())
432 			 && getPropertiesByPropertyDescriptor().equals(that.getPropertiesByPropertyDescriptor());
433 		 }
434 
435 		 return equality;
436 	 }
437 
438 	 /**
439 	  * @see #equals(Object)
440 	  */
441 	 @Override
442 	 public int hashCode() {
443 		 Object propertyValues = getPropertiesByPropertyDescriptor();
444 		 return getClass().getName().hashCode() + (getName() != null ? getName().hashCode() : 0)
445 		 + getPriority().hashCode() + (propertyValues != null ? propertyValues.hashCode() : 0);
446 	 }
447 }