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.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         // Anything from an interface is necessarily static and final
118         // Anything inside an annotation type is also static and final
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         // Get the variable name
169         String varName = variableDeclaratorId.getImage();
170 
171         // Skip serialVersionUID
172         if (varName.equals("serialVersionUID")) {
173             return data;
174         }
175 
176         // Static finals should be uppercase
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 }