1
2
3
4 package net.sourceforge.pmd.lang.java.rule.naming;
5
6 import net.sourceforge.pmd.PropertyDescriptor;
7 import net.sourceforge.pmd.lang.ast.Node;
8 import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration;
9 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
10 import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
11 import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
12 import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
13 import net.sourceforge.pmd.lang.java.ast.ASTFormalParameters;
14 import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration;
15 import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
16 import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;
17 import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
18 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
19 import net.sourceforge.pmd.lang.rule.properties.BooleanProperty;
20 import net.sourceforge.pmd.lang.rule.properties.StringMultiProperty;
21 import net.sourceforge.pmd.util.CollectionUtil;
22
23 public class VariableNamingConventionsRule extends AbstractJavaRule {
24
25 private boolean checkMembers;
26 private boolean checkLocals;
27 private boolean checkParameters;
28 private boolean checkNativeMethodParameters;
29 private String[] staticPrefixes;
30 private String[] staticSuffixes;
31 private String[] memberPrefixes;
32 private String[] memberSuffixes;
33 private String[] localPrefixes;
34 private String[] localSuffixes;
35 private String[] parameterPrefixes;
36 private String[] parameterSuffixes;
37
38 private static final BooleanProperty CHECK_MEMBERS_DESCRIPTOR = new BooleanProperty("checkMembers",
39 "Check member variables", true, 1.0f);
40
41 private static final BooleanProperty CHECK_LOCALS_DESCRIPTOR = new BooleanProperty("checkLocals",
42 "Check local variables", true, 2.0f);
43
44 private static final BooleanProperty CHECK_PARAMETERS_DESCRIPTOR = new BooleanProperty("checkParameters",
45 "Check constructor and method parameter variables", true, 3.0f);
46
47 private static final BooleanProperty CHECK_NATIVE_METHOD_PARAMETERS_DESCRIPTOR = new BooleanProperty(
48 "checkNativeMethodParameters", "Check method parameter of native methods", true, 3.5f);
49
50 private static final StringMultiProperty STATIC_PREFIXES_DESCRIPTOR = new StringMultiProperty("staticPrefix",
51 "Static variable prefixes", new String[] { "" }, 4.0f, ',');
52
53 private static final StringMultiProperty STATIC_SUFFIXES_DESCRIPTOR = new StringMultiProperty("staticSuffix",
54 "Static variable suffixes", new String[] { "" }, 5.0f, ',');
55
56 private static final StringMultiProperty MEMBER_PREFIXES_DESCRIPTOR = new StringMultiProperty("memberPrefix",
57 "Member variable prefixes", new String[] { "" }, 6.0f, ',');
58
59 private static final StringMultiProperty MEMBER_SUFFIXES_DESCRIPTOR = new StringMultiProperty("memberSuffix",
60 "Member variable suffixes", new String[] { "" }, 7.0f, ',');
61
62 private static final StringMultiProperty LOCAL_PREFIXES_DESCRIPTOR = new StringMultiProperty("localPrefix",
63 "Local variable prefixes", new String[] { "" }, 8.0f, ',');
64
65 private static final StringMultiProperty LOCAL_SUFFIXES_DESCRIPTOR = new StringMultiProperty("localSuffix",
66 "Local variable suffixes", new String[] { "" }, 9.0f, ',');
67
68 private static final StringMultiProperty PARAMETER_PREFIXES_DESCRIPTOR = new StringMultiProperty("parameterPrefix",
69 "Method parameter variable prefixes", new String[] { "" }, 10.0f, ',');
70
71 private static final StringMultiProperty PARAMETER_SUFFIXES_DESCRIPTOR = new StringMultiProperty("parameterSuffix",
72 "Method parameter variable suffixes", new String[] { "" }, 11.0f, ',');
73
74 public VariableNamingConventionsRule() {
75 definePropertyDescriptor(CHECK_MEMBERS_DESCRIPTOR);
76 definePropertyDescriptor(CHECK_LOCALS_DESCRIPTOR);
77 definePropertyDescriptor(CHECK_PARAMETERS_DESCRIPTOR);
78 definePropertyDescriptor(CHECK_NATIVE_METHOD_PARAMETERS_DESCRIPTOR);
79 definePropertyDescriptor(STATIC_PREFIXES_DESCRIPTOR);
80 definePropertyDescriptor(STATIC_SUFFIXES_DESCRIPTOR);
81 definePropertyDescriptor(MEMBER_PREFIXES_DESCRIPTOR);
82 definePropertyDescriptor(MEMBER_SUFFIXES_DESCRIPTOR);
83 definePropertyDescriptor(LOCAL_PREFIXES_DESCRIPTOR);
84 definePropertyDescriptor(LOCAL_SUFFIXES_DESCRIPTOR);
85 definePropertyDescriptor(PARAMETER_PREFIXES_DESCRIPTOR);
86 definePropertyDescriptor(PARAMETER_SUFFIXES_DESCRIPTOR);
87 }
88
89 public Object visit(ASTCompilationUnit node, Object data) {
90 init();
91 return super.visit(node, data);
92 }
93
94 protected void init() {
95 checkMembers = getProperty(CHECK_MEMBERS_DESCRIPTOR);
96 checkLocals = getProperty(CHECK_LOCALS_DESCRIPTOR);
97 checkParameters = getProperty(CHECK_PARAMETERS_DESCRIPTOR);
98 checkNativeMethodParameters = getProperty(CHECK_NATIVE_METHOD_PARAMETERS_DESCRIPTOR);
99 staticPrefixes = getProperty(STATIC_PREFIXES_DESCRIPTOR);
100 staticSuffixes = getProperty(STATIC_SUFFIXES_DESCRIPTOR);
101 memberPrefixes = getProperty(MEMBER_PREFIXES_DESCRIPTOR);
102 memberSuffixes = getProperty(MEMBER_SUFFIXES_DESCRIPTOR);
103 localPrefixes = getProperty(LOCAL_PREFIXES_DESCRIPTOR);
104 localSuffixes = getProperty(LOCAL_SUFFIXES_DESCRIPTOR);
105 parameterPrefixes = getProperty(PARAMETER_PREFIXES_DESCRIPTOR);
106 parameterSuffixes = getProperty(PARAMETER_SUFFIXES_DESCRIPTOR);
107 }
108
109 public Object visit(ASTFieldDeclaration node, Object data) {
110 if (!checkMembers) {
111 return data;
112 }
113 boolean isStatic = node.isStatic();
114 boolean isFinal = node.isFinal();
115
116 Node type = node.jjtGetParent().jjtGetParent().jjtGetParent();
117
118
119 if (type instanceof ASTClassOrInterfaceDeclaration && ((ASTClassOrInterfaceDeclaration) type).isInterface()
120 || type instanceof ASTAnnotationTypeDeclaration) {
121 isStatic = true;
122 isFinal = true;
123 }
124 return checkVariableDeclarators(node.isStatic() ? staticPrefixes : memberPrefixes, isStatic ? staticSuffixes
125 : memberSuffixes, node, isStatic, isFinal, data);
126 }
127
128 public Object visit(ASTLocalVariableDeclaration node, Object data) {
129 if (!checkLocals) {
130 return data;
131 }
132 return checkVariableDeclarators(localPrefixes, localSuffixes, node, false, node.isFinal(), data);
133 }
134
135 public Object visit(ASTFormalParameters node, Object data) {
136 if (!checkParameters) {
137 return data;
138 }
139 ASTMethodDeclaration methodDeclaration = node.getFirstParentOfType(ASTMethodDeclaration.class);
140 if (!checkNativeMethodParameters && methodDeclaration.isNative()) {
141 return data;
142 }
143
144 for (ASTFormalParameter formalParameter : node.findChildrenOfType(ASTFormalParameter.class)) {
145 for (ASTVariableDeclaratorId variableDeclaratorId : formalParameter
146 .findChildrenOfType(ASTVariableDeclaratorId.class)) {
147 checkVariableDeclaratorId(parameterPrefixes, parameterSuffixes, node, false, formalParameter.isFinal(),
148 variableDeclaratorId, data);
149 }
150 }
151 return data;
152 }
153
154 private Object checkVariableDeclarators(String[] prefixes, String[] suffixes, Node root, boolean isStatic,
155 boolean isFinal, Object data) {
156 for (ASTVariableDeclarator variableDeclarator : root.findChildrenOfType(ASTVariableDeclarator.class)) {
157 for (ASTVariableDeclaratorId variableDeclaratorId : variableDeclarator
158 .findChildrenOfType(ASTVariableDeclaratorId.class)) {
159 checkVariableDeclaratorId(prefixes, suffixes, root, isStatic, isFinal, variableDeclaratorId, data);
160 }
161 }
162 return data;
163 }
164
165 private Object checkVariableDeclaratorId(String[] prefixes, String[] suffixes, Node root, boolean isStatic,
166 boolean isFinal, ASTVariableDeclaratorId variableDeclaratorId, Object data) {
167
168
169 String varName = variableDeclaratorId.getImage();
170
171
172 if (varName.equals("serialVersionUID")) {
173 return data;
174 }
175
176
177 if (isStatic && isFinal) {
178 if (!varName.equals(varName.toUpperCase())) {
179 addViolationWithMessage(data, variableDeclaratorId,
180 "Variables that are final and static should be all capitals, ''{0}'' is not all capitals.",
181 new Object[] { varName });
182 }
183 return data;
184 } else if (!isFinal) {
185 String normalizedVarName = normalizeVariableName(varName, prefixes, suffixes);
186
187 if (normalizedVarName.indexOf('_') >= 0) {
188 addViolationWithMessage(
189 data,
190 variableDeclaratorId,
191 "Only variables that are final should contain underscores (except for underscores in standard prefix/suffix), ''{0}'' is not final.",
192 new Object[] { varName });
193 }
194 if (Character.isUpperCase(varName.charAt(0))) {
195 addViolationWithMessage(data, variableDeclaratorId,
196 "Variables should start with a lowercase character, ''{0}'' starts with uppercase character.",
197 new Object[] { varName });
198 }
199 }
200 return data;
201 }
202
203 private String normalizeVariableName(String varName, String[] prefixes, String[] suffixes) {
204 return stripSuffix(stripPrefix(varName, prefixes), suffixes);
205 }
206
207 private String stripSuffix(String varName, String[] suffixes) {
208 if (suffixes != null) {
209 for (int i = 0; i < suffixes.length; i++) {
210 if (varName.endsWith(suffixes[i])) {
211 varName = varName.substring(0, varName.length() - suffixes[i].length());
212 break;
213 }
214 }
215 }
216 return varName;
217 }
218
219 private String stripPrefix(String varName, String[] prefixes) {
220 if (prefixes != null) {
221 for (int i = 0; i < prefixes.length; i++) {
222 if (varName.startsWith(prefixes[i])) {
223 return varName.substring(prefixes[i].length());
224 }
225 }
226 }
227 return varName;
228 }
229
230 public boolean hasPrefixesOrSuffixes() {
231
232 for (PropertyDescriptor<?> desc : getPropertyDescriptors()) {
233 if (desc instanceof StringMultiProperty) {
234 String[] values = getProperty((StringMultiProperty) desc);
235 if (CollectionUtil.isNotEmpty(values)) {
236 return true;
237 }
238 }
239 }
240 return false;
241 }
242
243 public String dysfunctionReason() {
244 return hasPrefixesOrSuffixes() ? null : "No prefixes or suffixes specified";
245 }
246
247 }