/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.rule.style;
import static apex.jorje.semantic.symbol.type.ModifierTypeInfos.FINAL;
import static apex.jorje.semantic.symbol.type.ModifierTypeInfos.STATIC;
import net.sourceforge.pmd.PropertyDescriptor;
import net.sourceforge.pmd.lang.apex.ast.ASTField;
import net.sourceforge.pmd.lang.apex.ast.ASTParameter;
import net.sourceforge.pmd.lang.apex.ast.ASTUserClass;
import net.sourceforge.pmd.lang.apex.ast.ASTUserInterface;
import net.sourceforge.pmd.lang.apex.ast.ASTVariableDeclaration;
import net.sourceforge.pmd.lang.apex.ast.ApexNode;
import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule;
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 AbstractApexRule {
private boolean checkMembers;
private boolean checkLocals;
private boolean checkParameters;
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 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(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);
setProperty(CODECLIMATE_CATEGORIES, new String[] { "Style" });
// Note: x10 as Apex has not automatic refactoring
setProperty(CODECLIMATE_REMEDIATION_MULTIPLIER, 5);
setProperty(CODECLIMATE_BLOCK_HIGHLIGHTING, false);
}
public Object visit(ASTUserClass node, Object data) {
init();
return super.visit(node, data);
}
public Object visit(ASTUserInterface 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);
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(ASTField node, Object data) {
if (!checkMembers) {
return data;
}
boolean isStatic = node.getNode().getFieldInfo().getModifiers().has(STATIC);
boolean isFinal = node.getNode().getFieldInfo().getModifiers().has(FINAL);
return checkName(isStatic ? staticPrefixes : memberPrefixes, isStatic ? staticSuffixes : memberSuffixes, node,
isStatic, isFinal, data);
}
public Object visit(ASTVariableDeclaration node, Object data) {
if (!checkLocals) {
return data;
}
boolean isFinal = node.getNode().getLocalInfo().getModifiers().has(FINAL);
return checkName(localPrefixes, localSuffixes, node, false, isFinal, data);
}
public Object visit(ASTParameter node, Object data) {
if (!checkParameters) {
return data;
}
boolean isFinal = node.getNode().getModifierInfo().has(FINAL);
return checkName(parameterPrefixes, parameterSuffixes, node, false, isFinal, data);
}
private Object checkName(String[] prefixes, String[] suffixes, ApexNode<?> node, boolean isStatic, boolean isFinal,
Object data) {
String varName = node.getImage();
// Skip on null (with exception classes) and serialVersionUID
if (varName == null || "serialVersionUID".equals(varName)) {
return data;
}
// Static finals should be uppercase
if (isStatic && isFinal) {
if (!varName.equals(varName.toUpperCase())) {
addViolationWithMessage(data, node,
"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, node,
"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, node,
"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";
}
}