/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.rule;
import java.util.ArrayList;
import java.util.List;
import net.sourceforge.pmd.AbstractPropertySource;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.RulePriority;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.lang.ParserOptions;
import net.sourceforge.pmd.lang.ast.Node;
/**
* Basic abstract implementation of all parser-independent methods of the Rule
* interface.
*
* @author pieter_van_raemdonck - Application Engineers NV/SA - www.ae.be
*/
// FUTURE Implement Cloneable and clone()?
public abstract class AbstractRule extends AbstractPropertySource implements Rule {
private Language language;
private LanguageVersion minimumLanguageVersion;
private LanguageVersion maximumLanguageVersion;
private boolean deprecated;
private String name = getClass().getName();
private String since;
private String ruleClass = getClass().getName();
private String ruleSetName;
private String message;
private String description;
private List<String> examples = new ArrayList<>();
private String externalInfoUrl;
private RulePriority priority = RulePriority.LOW;
private boolean usesDFA;
private boolean usesTypeResolution;
private List<String> ruleChainVisits = new ArrayList<>();
public AbstractRule() {
definePropertyDescriptor(Rule.VIOLATION_SUPPRESS_REGEX_DESCRIPTOR);
definePropertyDescriptor(Rule.VIOLATION_SUPPRESS_XPATH_DESCRIPTOR);
}
public void deepCopyValuesTo(AbstractRule otherRule) {
otherRule.language = language;
otherRule.minimumLanguageVersion = minimumLanguageVersion;
otherRule.maximumLanguageVersion = maximumLanguageVersion;
otherRule.deprecated = deprecated;
otherRule.name = name;
otherRule.since = since;
otherRule.ruleClass = ruleClass;
otherRule.ruleSetName = ruleSetName;
otherRule.message = message;
otherRule.description = description;
otherRule.examples = copyExamples();
otherRule.externalInfoUrl = externalInfoUrl;
otherRule.priority = priority;
otherRule.propertyDescriptors = copyPropertyDescriptors();
otherRule.propertyValuesByDescriptor = copyPropertyValues();
otherRule.usesDFA = usesDFA;
otherRule.usesTypeResolution = usesTypeResolution;
otherRule.ruleChainVisits = copyRuleChainVisits();
}
private List<String> copyExamples() {
return new ArrayList<>(examples);
}
private List<String> copyRuleChainVisits() {
return new ArrayList<>(ruleChainVisits);
}
/**
* @see Rule#getLanguage()
*/
@Override
public Language getLanguage() {
return language;
}
/**
* @see Rule#setLanguage(net.sourceforge.pmd.lang.Language)
*/
@Override
public void setLanguage(Language language) {
if (this.language != null && this instanceof ImmutableLanguage && !this.language.equals(language)) {
throw new UnsupportedOperationException("The Language for Rule class " + this.getClass().getName()
+ " is immutable and cannot be changed.");
}
this.language = language;
}
/**
* @see Rule#getMinimumLanguageVersion()
*/
@Override
public LanguageVersion getMinimumLanguageVersion() {
return minimumLanguageVersion;
}
/**
* @see Rule#setMinimumLanguageVersion(net.sourceforge.pmd.lang.LanguageVersion)
*/
@Override
public void setMinimumLanguageVersion(LanguageVersion minimumLanguageVersion) {
this.minimumLanguageVersion = minimumLanguageVersion;
}
/**
* @see Rule#getMaximumLanguageVersion()
*/
@Override
public LanguageVersion getMaximumLanguageVersion() {
return maximumLanguageVersion;
}
/**
* @see Rule#setMaximumLanguageVersion(net.sourceforge.pmd.lang.LanguageVersion)
*/
@Override
public void setMaximumLanguageVersion(LanguageVersion maximumLanguageVersion) {
this.maximumLanguageVersion = maximumLanguageVersion;
}
/**
* @see Rule#isDeprecated()
*/
@Override
public boolean isDeprecated() {
return deprecated;
}
/**
* @see Rule#setDeprecated(boolean)
*/
@Override
public void setDeprecated(boolean deprecated) {
this.deprecated = deprecated;
}
/**
* @see Rule#getName()
*/
@Override
public String getName() {
return name;
}
/**
* @see Rule#setName(String)
*/
@Override
public void setName(String name) {
this.name = name;
}
/**
* @see Rule#getSince()
*/
@Override
public String getSince() {
return since;
}
/**
* @see Rule#setSince(String)
*/
@Override
public void setSince(String since) {
this.since = since;
}
/**
* @see Rule#getRuleClass()
*/
@Override
public String getRuleClass() {
return ruleClass;
}
/**
* @see Rule#setRuleClass(String)
*/
@Override
public void setRuleClass(String ruleClass) {
this.ruleClass = ruleClass;
}
/**
* @see Rule#getRuleSetName()
*/
@Override
public String getRuleSetName() {
return ruleSetName;
}
/**
* @see Rule#setRuleSetName(String)
*/
@Override
public void setRuleSetName(String ruleSetName) {
this.ruleSetName = ruleSetName;
}
/**
* @see Rule#getMessage()
*/
@Override
public String getMessage() {
return message;
}
/**
* @see Rule#setMessage(String)
*/
@Override
public void setMessage(String message) {
this.message = message;
}
/**
* @see Rule#getDescription()
*/
@Override
public String getDescription() {
return description;
}
/**
* @see Rule#setDescription(String)
*/
@Override
public void setDescription(String description) {
this.description = description;
}
/**
* @see Rule#getExamples()
*/
@Override
public List<String> getExamples() {
// TODO Needs to be externally immutable
return examples;
}
/**
* @see Rule#addExample(String)
*/
@Override
public void addExample(String example) {
examples.add(example);
}
/**
* @see Rule#getExternalInfoUrl()
*/
@Override
public String getExternalInfoUrl() {
return externalInfoUrl;
}
/**
* @see Rule#setExternalInfoUrl(String)
*/
@Override
public void setExternalInfoUrl(String externalInfoUrl) {
this.externalInfoUrl = externalInfoUrl;
}
/**
* @see Rule#getPriority()
*/
@Override
public RulePriority getPriority() {
return priority;
}
/**
* @see Rule#setPriority(RulePriority)
*/
@Override
public void setPriority(RulePriority priority) {
this.priority = priority;
}
/**
* This implementation returns a new instance of {@link ParserOptions} using
* default settings.
*
* @see Rule#setPriority(RulePriority)
*/
@Override
public ParserOptions getParserOptions() {
return new ParserOptions();
}
/**
* @see Rule#setUsesDFA()
*/
@Override
public void setUsesDFA() {
usesDFA = true;
}
/**
* @see Rule#usesDFA()
*/
@Override
public boolean usesDFA() {
return usesDFA;
}
/**
* @see Rule#setUsesTypeResolution()
*/
@Override
public void setUsesTypeResolution() {
usesTypeResolution = true;
}
/**
* @see Rule#usesTypeResolution()
*/
@Override
public boolean usesTypeResolution() {
return usesTypeResolution;
}
/**
* @see Rule#usesRuleChain()
*/
@Override
public boolean usesRuleChain() {
return !getRuleChainVisits().isEmpty();
}
/**
* @see Rule#getRuleChainVisits()
*/
@Override
public List<String> getRuleChainVisits() {
return ruleChainVisits;
}
/**
* @see Rule#addRuleChainVisit(Class)
*/
@Override
public void addRuleChainVisit(Class<? extends Node> nodeClass) {
if (!nodeClass.getSimpleName().startsWith("AST")) {
throw new IllegalArgumentException("Node class does not start with 'AST' prefix: " + nodeClass);
}
addRuleChainVisit(nodeClass.getSimpleName().substring("AST".length()));
}
/**
* @see Rule#addRuleChainVisit(String)
*/
@Override
public void addRuleChainVisit(String astNodeName) {
if (!ruleChainVisits.contains(astNodeName)) {
ruleChainVisits.add(astNodeName);
}
}
/**
* @see Rule#start(RuleContext)
*/
@Override
public void start(RuleContext ctx) {
// Override as needed
}
/**
* @see Rule#end(RuleContext)
*/
@Override
public void end(RuleContext ctx) {
// Override as needed
}
/**
* @see RuleViolationFactory#addViolation(RuleContext, Rule, Node, String,
* Object[])
*/
public void addViolation(Object data, Node node) {
RuleContext ruleContext = (RuleContext) data;
ruleContext.getLanguageVersion().getLanguageVersionHandler().getRuleViolationFactory().addViolation(ruleContext,
this, node, this.getMessage(), null);
}
/**
* @see RuleViolationFactory#addViolation(RuleContext, Rule, Node, String,
* Object[])
*/
public void addViolation(Object data, Node node, String arg) {
RuleContext ruleContext = (RuleContext) data;
ruleContext.getLanguageVersion().getLanguageVersionHandler().getRuleViolationFactory().addViolation(ruleContext,
this, node, this.getMessage(), new Object[] { arg });
}
/**
* @see RuleViolationFactory#addViolation(RuleContext, Rule, Node, String,
* Object[])
*/
public void addViolation(Object data, Node node, Object[] args) {
RuleContext ruleContext = (RuleContext) data;
ruleContext.getLanguageVersion().getLanguageVersionHandler().getRuleViolationFactory().addViolation(ruleContext,
this, node, this.getMessage(), args);
}
/**
* @see RuleViolationFactory#addViolation(RuleContext, Rule, Node, String,
* Object[])
*/
public void addViolationWithMessage(Object data, Node node, String message) {
RuleContext ruleContext = (RuleContext) data;
ruleContext.getLanguageVersion().getLanguageVersionHandler().getRuleViolationFactory().addViolation(ruleContext,
this, node, message, null);
}
/**
* @see RuleViolationFactory#addViolation(RuleContext, Rule, Node, String,
* Object[])
*/
public void addViolationWithMessage(Object data, Node node, String message, int beginLine, int endLine) {
RuleContext ruleContext = (RuleContext) data;
ruleContext.getLanguageVersion().getLanguageVersionHandler().getRuleViolationFactory().addViolation(ruleContext,
this, node, message, beginLine, endLine, null);
}
/**
* @see RuleViolationFactory#addViolation(RuleContext, Rule, Node, String,
* Object[])
*/
public void addViolationWithMessage(Object data, Node node, String message, Object[] args) {
RuleContext ruleContext = (RuleContext) data;
ruleContext.getLanguageVersion().getLanguageVersionHandler().getRuleViolationFactory().addViolation(ruleContext,
this, node, message, args);
}
/**
* Rules are equal if:
* <ol>
* <li>They have the same implementation class.</li>
* <li>They have the same name.</li>
* <li>They have the same priority.</li>
* <li>They share the same properties.</li>
* </ol>
*/
@Override
public boolean equals(Object o) {
if (o == null) {
return false; // trivial
}
if (this == o) {
return true; // trivial
}
boolean equality = getClass() == o.getClass();
if (equality) {
Rule that = (Rule) o;
equality = getName().equals(that.getName()) && getPriority().equals(that.getPriority())
&& getPropertiesByPropertyDescriptor().equals(that.getPropertiesByPropertyDescriptor());
}
return equality;
}
/**
* @see #equals(Object)
*/
@Override
public int hashCode() {
Object propertyValues = getPropertiesByPropertyDescriptor();
return getClass().getName().hashCode() + (getName() != null ? getName().hashCode() : 0)
+ getPriority().hashCode() + (propertyValues != null ? propertyValues.hashCode() : 0);
}
}