View Javadoc
1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
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         // Anything from an interface is necessarily static and final
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         // Get the variable name
165         String varName = variableDeclaratorId.getImage();
166 
167         // Skip serialVersionUID
168         if (varName.equals("serialVersionUID")) {
169             return data;
170         }
171 
172         // Static finals should be uppercase
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 }