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