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<String>();
    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<String>();
    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<String>(examples.size());
    72  		copy.addAll(examples);
    73  		return copy;
    74  	}
    75
    76  	private List<String> copyRuleChainVisits() {
    77  		List<String> copy = new ArrayList<String>(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(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(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(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 }