VariableNamingConventionsRule.java
001 /**
002  * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003  */
004 package net.sourceforge.pmd.lang.java.rule.naming;
005 
006 import net.sourceforge.pmd.lang.ast.Node;
007 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
008 import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
009 import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
010 import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
011 import net.sourceforge.pmd.lang.java.ast.ASTFormalParameters;
012 import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration;
013 import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;
014 import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
015 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
016 import net.sourceforge.pmd.lang.rule.properties.BooleanProperty;
017 import net.sourceforge.pmd.lang.rule.properties.StringMultiProperty;
018 
019 public class VariableNamingConventionsRule extends AbstractJavaRule {
020 
021     private boolean checkMembers;
022     private boolean checkLocals;
023     private boolean checkParameters;
024     private String[] staticPrefixes;
025     private String[] staticSuffixes;
026     private String[] memberPrefixes;
027     private String[] memberSuffixes;
028     private String[] localPrefixes;
029     private String[] localSuffixes;
030     private String[] parameterPrefixes;
031     private String[] parameterSuffixes;
032 
033     private static final BooleanProperty CHECK_MEMBERS_DESCRIPTOR = new BooleanProperty("checkMembers",
034       "Check member variables", true, 1.0f);
035 
036     private static final BooleanProperty CHECK_LOCALS_DESCRIPTOR = new BooleanProperty("checkLocals",
037       "Check local variables", true, 2.0f);
038 
039     private static final BooleanProperty CHECK_PARAMETERS_DESCRIPTOR = new BooleanProperty("checkParameters",
040       "Check constructor and method parameter variables", true, 3.0f);
041 
042     private static final StringMultiProperty STATIC_PREFIXES_DESCRIPTOR = new StringMultiProperty("staticPrefix",
043       "Static variable prefixes"new String[] { "" }4.0f',');
044 
045     private static final StringMultiProperty STATIC_SUFFIXES_DESCRIPTOR = new StringMultiProperty("staticSuffix",
046       "Static variable suffixes"new String[] { "" }5.0f',');
047 
048     private static final StringMultiProperty MEMBER_PREFIXES_DESCRIPTOR = new StringMultiProperty("memberPrefix",
049       "Member variable prefixes"new String[] { "" }6.0f',');
050 
051     private static final StringMultiProperty MEMBER_SUFFIXES_DESCRIPTOR = new StringMultiProperty("memberSuffix",
052       "Member variable suffixes"new String[] { "" }7.0f',');
053 
054     private static final StringMultiProperty LOCAL_PREFIXES_DESCRIPTOR = new StringMultiProperty("localPrefix",
055       "Local variable prefixes"new String[] { "" }8.0f',');
056 
057     private static final StringMultiProperty LOCAL_SUFFIXES_DESCRIPTOR = new StringMultiProperty("localSuffix",
058       "Local variable suffixes"new String[] { "" }9.0f',');
059 
060     private static final StringMultiProperty PARAMETER_PREFIXES_DESCRIPTOR = new StringMultiProperty("parameterPrefix",
061       "Method parameter variable prefixes"new String[] { "" }10.0f',');
062 
063     private static final StringMultiProperty PARAMETER_SUFFIXES_DESCRIPTOR = new StringMultiProperty("parameterSuffix",
064       "Method parameter variable suffixes"new String[] { "" }11.0f',');
065 
066     public VariableNamingConventionsRule() {
067   definePropertyDescriptor(CHECK_MEMBERS_DESCRIPTOR);
068   definePropertyDescriptor(CHECK_LOCALS_DESCRIPTOR);
069   definePropertyDescriptor(CHECK_PARAMETERS_DESCRIPTOR);
070   definePropertyDescriptor(STATIC_PREFIXES_DESCRIPTOR);
071   definePropertyDescriptor(STATIC_SUFFIXES_DESCRIPTOR);
072   definePropertyDescriptor(MEMBER_PREFIXES_DESCRIPTOR);
073   definePropertyDescriptor(MEMBER_SUFFIXES_DESCRIPTOR);
074   definePropertyDescriptor(LOCAL_PREFIXES_DESCRIPTOR);
075   definePropertyDescriptor(LOCAL_SUFFIXES_DESCRIPTOR);
076   definePropertyDescriptor(PARAMETER_PREFIXES_DESCRIPTOR);
077   definePropertyDescriptor(PARAMETER_SUFFIXES_DESCRIPTOR);
078     }
079 
080     public Object visit(ASTCompilationUnit node, Object data) {
081   init();
082   return super.visit(node, data);
083     }
084 
085     protected void init() {
086   checkMembers = getProperty(CHECK_MEMBERS_DESCRIPTOR);
087   checkLocals = getProperty(CHECK_LOCALS_DESCRIPTOR);
088   checkParameters = getProperty(CHECK_PARAMETERS_DESCRIPTOR);
089   staticPrefixes = getProperty(STATIC_PREFIXES_DESCRIPTOR);
090   staticSuffixes = getProperty(STATIC_SUFFIXES_DESCRIPTOR);
091   memberPrefixes = getProperty(MEMBER_PREFIXES_DESCRIPTOR);
092   memberSuffixes = getProperty(MEMBER_SUFFIXES_DESCRIPTOR);
093   localPrefixes = getProperty(LOCAL_PREFIXES_DESCRIPTOR);
094   localSuffixes = getProperty(LOCAL_SUFFIXES_DESCRIPTOR);
095   parameterPrefixes = getProperty(PARAMETER_PREFIXES_DESCRIPTOR);
096   parameterSuffixes = getProperty(PARAMETER_SUFFIXES_DESCRIPTOR);
097     }
098 
099     public Object visit(ASTFieldDeclaration node, Object data) {
100   if (!checkMembers) {
101       return data;
102   }
103   boolean isStatic = node.isStatic();
104   boolean isFinal = node.isFinal();
105   // Anything from an interface is necessarily static and final
106   if (node.jjtGetParent().jjtGetParent().jjtGetParent() instanceof ASTClassOrInterfaceDeclaration
107     && ((ASTClassOrInterfaceDeclarationnode.jjtGetParent().jjtGetParent().jjtGetParent()).isInterface()) {
108       isStatic = true;
109       isFinal = true;
110   }
111   return checkVariableDeclarators(node.isStatic() ? staticPrefixes : memberPrefixes, isStatic ? staticSuffixes
112     : memberSuffixes, node, isStatic, isFinal, data);
113     }
114 
115     public Object visit(ASTLocalVariableDeclaration node, Object data) {
116   if (!checkLocals) {
117       return data;
118   }
119   return checkVariableDeclarators(localPrefixes, localSuffixes, node, false, node.isFinal(), data);
120     }
121 
122     public Object visit(ASTFormalParameters node, Object data) {
123   if (!checkParameters) {
124       return data;
125   }
126   for (ASTFormalParameter formalParameter : node.findChildrenOfType(ASTFormalParameter.class)) {
127       for (ASTVariableDeclaratorId variableDeclaratorId : formalParameter
128         .findChildrenOfType(ASTVariableDeclaratorId.class)) {
129     checkVariableDeclaratorId(parameterPrefixes, parameterSuffixes, node, false, formalParameter.isFinal(),
130       variableDeclaratorId, data);
131       }
132   }
133   return data;
134     }
135 
136     private Object checkVariableDeclarators(String[] prefixes, String[] suffixes, Node root, boolean isStatic,
137       boolean isFinal, Object data) {
138   for (ASTVariableDeclarator variableDeclarator : root.findChildrenOfType(ASTVariableDeclarator.class)) {
139       for (ASTVariableDeclaratorId variableDeclaratorId : variableDeclarator
140         .findChildrenOfType(ASTVariableDeclaratorId.class)) {
141     checkVariableDeclaratorId(prefixes, suffixes, root, isStatic, isFinal, variableDeclaratorId, data);
142       }
143   }
144   return data;
145     }
146 
147     private Object checkVariableDeclaratorId(String[] prefixes, String[] suffixes, Node root, boolean isStatic,
148       boolean isFinal, ASTVariableDeclaratorId variableDeclaratorId, Object data) {
149 
150   // Get the variable name
151   String varName = variableDeclaratorId.getImage();
152 
153   // Skip serialVersionUID
154   if (varName.equals("serialVersionUID")) {
155       return data;
156   }
157 
158   // Static finals should be uppercase
159   if (isStatic && isFinal) {
160       if (!varName.equals(varName.toUpperCase())) {
161     addViolationWithMessage(data, variableDeclaratorId,
162       "Variables that are final and static should be all capitals, ''{0}'' is not all capitals.",
163       new Object[] { varName });
164       }
165       return data;
166   else if (!isFinal) {
167       String normalizedVarName = normalizeVariableName(varName, prefixes, suffixes);
168 
169       if (normalizedVarName.indexOf('_'>= 0) {
170     addViolationWithMessage(
171       data,
172       variableDeclaratorId,
173       "Only variables that are final should contain underscores (except for underscores in standard prefix/suffix), ''{0}'' is not final.",
174       new Object[] { varName });
175       }
176       if (Character.isUpperCase(varName.charAt(0))) {
177     addViolationWithMessage(data, variableDeclaratorId,
178       "Variables should start with a lowercase character, ''{0}'' starts with uppercase character.",
179       new Object[] { varName });
180       }
181   }
182   return data;
183     }
184 
185     private String normalizeVariableName(String varName, String[] prefixes, String[] suffixes) {
186   return stripSuffix(stripPrefix(varName, prefixes), suffixes);
187     }
188 
189     private String stripSuffix(String varName, String[] suffixes) {
190   if (suffixes != null) {
191       for (int i = 0; i < suffixes.length; i++) {
192     if (varName.endsWith(suffixes[i])) {
193         varName = varName.substring(0, varName.length() - suffixes[i].length());
194         break;
195     }
196       }
197   }
198   return varName;
199     }
200 
201     private String stripPrefix(String varName, String[] prefixes) {
202   if (prefixes != null) {
203       for (int i = 0; i < prefixes.length; i++) {
204     if (varName.startsWith(prefixes[i])) {
205         return varName.substring(prefixes[i].length());
206     }
207       }
208   }
209   return varName;
210     }
211 }