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.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 	// Anything from an interface is necessarily static and final
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 	// Get the variable name
153 	String varName = variableDeclaratorId.getImage();
154 
155 	// Skip serialVersionUID
156 	if (varName.equals("serialVersionUID")) {
157 	    return data;
158 	}
159 
160 	// Static finals should be uppercase
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 }