/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.rule.naming;
import net.sourceforge.pmd.PropertyDescriptor;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
import net.sourceforge.pmd.lang.java.ast.ASTFormalParameters;
import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
import net.sourceforge.pmd.lang.rule.properties.BooleanProperty;
import net.sourceforge.pmd.lang.rule.properties.StringMultiProperty;
import net.sourceforge.pmd.util.CollectionUtil;
public class VariableNamingConventionsRule extends AbstractJavaRule {
private boolean checkMembers;
private boolean checkLocals;
private boolean checkParameters;
private boolean checkNativeMethodParameters;
private String[] staticPrefixes;
private String[] staticSuffixes;
private String[] memberPrefixes;
private String[] memberSuffixes;
private String[] localPrefixes;
private String[] localSuffixes;
private String[] parameterPrefixes;
private String[] parameterSuffixes;
private static final BooleanProperty CHECK_MEMBERS_DESCRIPTOR = new BooleanProperty("checkMembers",
"Check member variables", true, 1.0f);
private static final BooleanProperty CHECK_LOCALS_DESCRIPTOR = new BooleanProperty("checkLocals",
"Check local variables", true, 2.0f);
private static final BooleanProperty CHECK_PARAMETERS_DESCRIPTOR = new BooleanProperty("checkParameters",
"Check constructor and method parameter variables", true, 3.0f);
private static final BooleanProperty CHECK_NATIVE_METHOD_PARAMETERS_DESCRIPTOR = new BooleanProperty(
"checkNativeMethodParameters", "Check method parameter of native methods", true, 3.5f);
private static final StringMultiProperty STATIC_PREFIXES_DESCRIPTOR = new StringMultiProperty("staticPrefix",
"Static variable prefixes", new String[] { "" }, 4.0f, ',');
private static final StringMultiProperty STATIC_SUFFIXES_DESCRIPTOR = new StringMultiProperty("staticSuffix",
"Static variable suffixes", new String[] { "" }, 5.0f, ',');
private static final StringMultiProperty MEMBER_PREFIXES_DESCRIPTOR = new StringMultiProperty("memberPrefix",
"Member variable prefixes", new String[] { "" }, 6.0f, ',');
private static final StringMultiProperty MEMBER_SUFFIXES_DESCRIPTOR = new StringMultiProperty("memberSuffix",
"Member variable suffixes", new String[] { "" }, 7.0f, ',');
private static final StringMultiProperty LOCAL_PREFIXES_DESCRIPTOR = new StringMultiProperty("localPrefix",
"Local variable prefixes", new String[] { "" }, 8.0f, ',');
private static final StringMultiProperty LOCAL_SUFFIXES_DESCRIPTOR = new StringMultiProperty("localSuffix",
"Local variable suffixes", new String[] { "" }, 9.0f, ',');
private static final StringMultiProperty PARAMETER_PREFIXES_DESCRIPTOR = new StringMultiProperty("parameterPrefix",
"Method parameter variable prefixes", new String[] { "" }, 10.0f, ',');
private static final StringMultiProperty PARAMETER_SUFFIXES_DESCRIPTOR = new StringMultiProperty("parameterSuffix",
"Method parameter variable suffixes", new String[] { "" }, 11.0f, ',');
public VariableNamingConventionsRule() {
definePropertyDescriptor(CHECK_MEMBERS_DESCRIPTOR);
definePropertyDescriptor(CHECK_LOCALS_DESCRIPTOR);
definePropertyDescriptor(CHECK_PARAMETERS_DESCRIPTOR);
definePropertyDescriptor(CHECK_NATIVE_METHOD_PARAMETERS_DESCRIPTOR);
definePropertyDescriptor(STATIC_PREFIXES_DESCRIPTOR);
definePropertyDescriptor(STATIC_SUFFIXES_DESCRIPTOR);
definePropertyDescriptor(MEMBER_PREFIXES_DESCRIPTOR);
definePropertyDescriptor(MEMBER_SUFFIXES_DESCRIPTOR);
definePropertyDescriptor(LOCAL_PREFIXES_DESCRIPTOR);
definePropertyDescriptor(LOCAL_SUFFIXES_DESCRIPTOR);
definePropertyDescriptor(PARAMETER_PREFIXES_DESCRIPTOR);
definePropertyDescriptor(PARAMETER_SUFFIXES_DESCRIPTOR);
}
public Object visit(ASTCompilationUnit node, Object data) {
init();
return super.visit(node, data);
}
protected void init() {
checkMembers = getProperty(CHECK_MEMBERS_DESCRIPTOR);
checkLocals = getProperty(CHECK_LOCALS_DESCRIPTOR);
checkParameters = getProperty(CHECK_PARAMETERS_DESCRIPTOR);
checkNativeMethodParameters = getProperty(CHECK_NATIVE_METHOD_PARAMETERS_DESCRIPTOR);
staticPrefixes = getProperty(STATIC_PREFIXES_DESCRIPTOR);
staticSuffixes = getProperty(STATIC_SUFFIXES_DESCRIPTOR);
memberPrefixes = getProperty(MEMBER_PREFIXES_DESCRIPTOR);
memberSuffixes = getProperty(MEMBER_SUFFIXES_DESCRIPTOR);
localPrefixes = getProperty(LOCAL_PREFIXES_DESCRIPTOR);
localSuffixes = getProperty(LOCAL_SUFFIXES_DESCRIPTOR);
parameterPrefixes = getProperty(PARAMETER_PREFIXES_DESCRIPTOR);
parameterSuffixes = getProperty(PARAMETER_SUFFIXES_DESCRIPTOR);
}
public Object visit(ASTFieldDeclaration node, Object data) {
if (!checkMembers) {
return data;
}
boolean isStatic = node.isStatic();
boolean isFinal = node.isFinal();
Node type = node.jjtGetParent().jjtGetParent().jjtGetParent();
// Anything from an interface is necessarily static and final
// Anything inside an annotation type is also static and final
if (type instanceof ASTClassOrInterfaceDeclaration && ((ASTClassOrInterfaceDeclaration) type).isInterface()
|| type instanceof ASTAnnotationTypeDeclaration) {
isStatic = true;
isFinal = true;
}
return checkVariableDeclarators(node.isStatic() ? staticPrefixes : memberPrefixes,
isStatic ? staticSuffixes : memberSuffixes, node, isStatic, isFinal, data);
}
public Object visit(ASTLocalVariableDeclaration node, Object data) {
if (!checkLocals) {
return data;
}
return checkVariableDeclarators(localPrefixes, localSuffixes, node, false, node.isFinal(), data);
}
public Object visit(ASTFormalParameters node, Object data) {
if (!checkParameters) {
return data;
}
ASTMethodDeclaration methodDeclaration = node.getFirstParentOfType(ASTMethodDeclaration.class);
if (!checkNativeMethodParameters && methodDeclaration.isNative()) {
return data;
}
for (ASTFormalParameter formalParameter : node.findChildrenOfType(ASTFormalParameter.class)) {
for (ASTVariableDeclaratorId variableDeclaratorId : formalParameter
.findChildrenOfType(ASTVariableDeclaratorId.class)) {
checkVariableDeclaratorId(parameterPrefixes, parameterSuffixes, node, false, formalParameter.isFinal(),
variableDeclaratorId, data);
}
}
return data;
}
private Object checkVariableDeclarators(String[] prefixes, String[] suffixes, Node root, boolean isStatic,
boolean isFinal, Object data) {
for (ASTVariableDeclarator variableDeclarator : root.findChildrenOfType(ASTVariableDeclarator.class)) {
for (ASTVariableDeclaratorId variableDeclaratorId : variableDeclarator
.findChildrenOfType(ASTVariableDeclaratorId.class)) {
checkVariableDeclaratorId(prefixes, suffixes, root, isStatic, isFinal, variableDeclaratorId, data);
}
}
return data;
}
private Object checkVariableDeclaratorId(String[] prefixes, String[] suffixes, Node root, boolean isStatic,
boolean isFinal, ASTVariableDeclaratorId variableDeclaratorId, Object data) {
// Get the variable name
String varName = variableDeclaratorId.getImage();
// Skip serialVersionUID
if ("serialVersionUID".equals(varName)) {
return data;
}
// Static finals should be uppercase
if (isStatic && isFinal) {
if (!varName.equals(varName.toUpperCase())) {
addViolationWithMessage(data, variableDeclaratorId,
"Variables that are final and static should be all capitals, ''{0}'' is not all capitals.",
new Object[] { varName });
}
return data;
} else if (!isFinal) {
String normalizedVarName = normalizeVariableName(varName, prefixes, suffixes);
if (normalizedVarName.indexOf('_') >= 0) {
addViolationWithMessage(data, variableDeclaratorId,
"Only variables that are final should contain underscores (except for underscores in standard prefix/suffix), ''{0}'' is not final.",
new Object[] { varName });
}
if (Character.isUpperCase(varName.charAt(0))) {
addViolationWithMessage(data, variableDeclaratorId,
"Variables should start with a lowercase character, ''{0}'' starts with uppercase character.",
new Object[] { varName });
}
}
return data;
}
private String normalizeVariableName(String varName, String[] prefixes, String[] suffixes) {
return stripSuffix(stripPrefix(varName, prefixes), suffixes);
}
private String stripSuffix(String varName, String[] suffixes) {
if (suffixes != null) {
for (int i = 0; i < suffixes.length; i++) {
if (varName.endsWith(suffixes[i])) {
varName = varName.substring(0, varName.length() - suffixes[i].length());
break;
}
}
}
return varName;
}
private String stripPrefix(String varName, String[] prefixes) {
if (prefixes != null) {
for (int i = 0; i < prefixes.length; i++) {
if (varName.startsWith(prefixes[i])) {
return varName.substring(prefixes[i].length());
}
}
}
return varName;
}
public boolean hasPrefixesOrSuffixes() {
for (PropertyDescriptor<?> desc : getPropertyDescriptors()) {
if (desc instanceof StringMultiProperty) {
String[] values = getProperty((StringMultiProperty) desc);
if (CollectionUtil.isNotEmpty(values)) {
return true;
}
}
}
return false;
}
public String dysfunctionReason() {
return hasPrefixesOrSuffixes() ? null : "No prefixes or suffixes specified";
}
}