/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.rule;
import java.util.regex.Pattern;
import net.sourceforge.pmd.PropertyDescriptor;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.util.StringUtil;
public class ParametricRuleViolation<T extends Node> implements RuleViolation {
protected final Rule rule;
protected final String description;
protected boolean suppressed;
protected String filename;
protected int beginLine;
protected int beginColumn;
protected int endLine;
protected int endColumn;
protected String packageName = "";
protected String className = "";
protected String methodName = "";
protected String variableName = "";
// FUTURE Fix to understand when a violation _must_ have a Node, and when it
// must not (to prevent erroneous Rules silently logging w/o a Node). Modify
// RuleViolationFactory to support identifying without a Node, and update
// Rule base classes too.
public ParametricRuleViolation(Rule theRule, RuleContext ctx, T node, String message) {
rule = theRule;
description = message;
filename = ctx.getSourceCodeFilename();
if (filename == null) {
filename = "";
}
if (node != null) {
beginLine = node.getBeginLine();
beginColumn = node.getBeginColumn();
endLine = node.getEndLine();
endColumn = node.getEndColumn();
}
// Apply Rule specific suppressions
if (node != null && rule != null) {
setSuppression(rule, node);
}
}
private void setSuppression(Rule rule, T node) {
String regex = rule.getProperty(Rule.VIOLATION_SUPPRESS_REGEX_DESCRIPTOR); // Regex
if (regex != null && description != null) {
if (Pattern.matches(regex, description)) {
suppressed = true;
}
}
if (!suppressed) { // XPath
String xpath = rule.getProperty(Rule.VIOLATION_SUPPRESS_XPATH_DESCRIPTOR);
if (xpath != null) {
suppressed = node.hasDescendantMatchingXPath(xpath);
}
}
}
protected String expandVariables(String message) {
if (message.indexOf("${") < 0) {
return message;
}
StringBuilder buf = new StringBuilder(message);
int startIndex = -1;
while ((startIndex = buf.indexOf("${", startIndex + 1)) >= 0) {
final int endIndex = buf.indexOf("}", startIndex);
if (endIndex >= 0) {
final String name = buf.substring(startIndex + 2, endIndex);
if (isVariable(name)) {
buf.replace(startIndex, endIndex + 1, getVariableValue(name));
}
}
}
return buf.toString();
}
protected boolean isVariable(String name) {
return StringUtil.isAnyOf(name, "variableName", "methodName", "className", "packageName")
|| rule.getPropertyDescriptor(name) != null;
}
protected String getVariableValue(String name) {
if ("variableName".equals(name)) {
return variableName;
} else if ("methodName".equals(name)) {
return methodName;
} else if ("className".equals(name)) {
return className;
} else if ("packageName".equals(name)) {
return packageName;
} else {
final PropertyDescriptor<?> propertyDescriptor = rule.getPropertyDescriptor(name);
return String.valueOf(rule.getProperty(propertyDescriptor));
}
}
@Override
public Rule getRule() {
return rule;
}
@Override
public String getDescription() {
return expandVariables(description);
}
@Override
public boolean isSuppressed() {
return suppressed;
}
@Override
public String getFilename() {
return filename;
}
@Override
public int getBeginLine() {
return beginLine;
}
@Override
public int getBeginColumn() {
return beginColumn;
}
@Override
public int getEndLine() {
return endLine;
}
@Override
public int getEndColumn() {
return endColumn;
}
@Override
public String getPackageName() {
return packageName;
}
@Override
public String getClassName() {
return className;
}
@Override
public String getMethodName() {
return methodName;
}
@Override
public String getVariableName() {
return variableName;
}
public void setLines(int theBeginLine, int theEndLine) {
beginLine = theBeginLine;
endLine = theEndLine;
}
@Override
public String toString() {
return getFilename() + ':' + getRule() + ':' + getDescription() + ':' + beginLine;
}
}