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