UselessOverridingMethod xref

View Javadoc
1 /**
2  * BSD-style license; for more info see http://pmd.sourceforge.net/license.html 
3  */
4 package net.sourceforge.pmd.rules;
5 
6 import java.util.ArrayList;
7 import java.util.List;
8 import java.util.Map;
9 
10 import net.sourceforge.pmd.AbstractRule;
11 import net.sourceforge.pmd.PropertyDescriptor;
12 import net.sourceforge.pmd.ast.ASTAnnotation;
13 import net.sourceforge.pmd.ast.ASTArgumentList;
14 import net.sourceforge.pmd.ast.ASTArguments;
15 import net.sourceforge.pmd.ast.ASTBlock;
16 import net.sourceforge.pmd.ast.ASTClassOrInterfaceBodyDeclaration;
17 import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
18 import net.sourceforge.pmd.ast.ASTFormalParameter;
19 import net.sourceforge.pmd.ast.ASTFormalParameters;
20 import net.sourceforge.pmd.ast.ASTImplementsList;
21 import net.sourceforge.pmd.ast.ASTMarkerAnnotation;
22 import net.sourceforge.pmd.ast.ASTMethodDeclaration;
23 import net.sourceforge.pmd.ast.ASTMethodDeclarator;
24 import net.sourceforge.pmd.ast.ASTName;
25 import net.sourceforge.pmd.ast.ASTNameList;
26 import net.sourceforge.pmd.ast.ASTPrimaryExpression;
27 import net.sourceforge.pmd.ast.ASTPrimaryPrefix;
28 import net.sourceforge.pmd.ast.ASTPrimarySuffix;
29 import net.sourceforge.pmd.ast.ASTResultType;
30 import net.sourceforge.pmd.ast.ASTStatement;
31 import net.sourceforge.pmd.ast.ASTVariableDeclaratorId;
32 import net.sourceforge.pmd.ast.Node;
33 import net.sourceforge.pmd.ast.SimpleNode;
34 import net.sourceforge.pmd.properties.BooleanProperty;
35 
36 import org.jaxen.JaxenException;
37 
38 /**
39  * @author Romain Pelisse, bugfix for [ 1522517 ] False +: UselessOverridingMethod
40  */
41 public class UselessOverridingMethod extends AbstractRule {
42 
43 private final List<String> exceptions;
44 private boolean ignoreAnnotations;
45 private static final String CLONE = "clone";
46 private static final String OBJECT = "Object";
47 
48 private static final PropertyDescriptor ignoreAnnotationsDescriptor = new BooleanProperty(
49 "ignoreAnnotations", "Ignore annotations", false, 1.0f);
50 
51 private static final Map<String, PropertyDescriptor> propertyDescriptorsByName = asFixedMap(new PropertyDescriptor[] { ignoreAnnotationsDescriptor });
52 
53 @Override
54 protected Map<String, PropertyDescriptor> propertiesByName() {
55 return propertyDescriptorsByName;
56 }
57 
58 public UselessOverridingMethod() {
59 exceptions = new ArrayList<String>(1);
60 exceptions.add("CloneNotSupportedException");
61 }
62 
63 	@Override
64 public Object visit(ASTImplementsList clz, Object data)
65 	{
66 		return super.visit(clz,data);
67 	}
68 
69 @Override
70 public Object visit(ASTClassOrInterfaceDeclaration clz, Object data) {
71 if (clz.isInterface()) {
72 return data;
73 }
74 ignoreAnnotations = getBooleanProperty(ignoreAnnotationsDescriptor);
75 return super.visit(clz, data);
76 }
77 
78 //TODO: this method should be externalize into an utility class, shouldn't it ?
79 private boolean isMethodType(ASTMethodDeclaration node,String methodType)
80 {
81 	boolean result = false;
82 	ASTResultType type = node.getResultType();
83 	if ( type != null ) {
84 		List results = null;
85 try {
86 	 results = type.findChildNodesWithXPath("./Type/ReferenceType/ClassOrInterfaceType[@Image = '" + methodType + "']");
87 }
88 catch (JaxenException e) {
89 	 e.printStackTrace();
90 }
91 		if ( results != null && results.size()> 0 ) {
92 			result = true;
93 		}
94 	}
95 	return result;
96 }
97 
98 //TODO: this method should be externalize into an utility class, shouldn't it ?
99 private boolean isMethodThrowingType(ASTMethodDeclaration node, List<String> exceptedExceptions) {
100 	boolean result = false;
101 	 ASTNameList thrownsExceptions = node.getFirstChildOfType(ASTNameList.class);
102 	 if ( thrownsExceptions != null ) {
103 	 	List<ASTName> names = thrownsExceptions.findChildrenOfType(ASTName.class);
104 	 	for ( ASTName name : names ) {
105 	 		for ( String exceptedException : exceptedExceptions) {
106 		 		if ( exceptedException.equals(name.getImage()) )
107 		 			result = true;
108 	 		}
109 	 	}
110 	 }
111 	 return result;
112 }
113 
114 	private boolean hasArguments(ASTMethodDeclaration node) {
115 		boolean result = false;
116 		try
117 		{
118 			List parameters = node.findChildNodesWithXPath("./MethodDeclarator/FormalParameters/*");
119 			if ( parameters != null && parameters.size()> 0 ) {
120 				result = true;
121 			}
122 		} catch (JaxenException e) {
123 			e.printStackTrace();
124 		}
125 		return result;
126 	}
127 
128 @Override
129 public Object visit(ASTMethodDeclaration node, Object data) {
130 // Can skip abstract methods and methods whose only purpose is to
131 // guarantee that the inherited method is not changed by finalizing
132 // them.
133 if (node.isAbstract() || node.isFinal() || node.isNative() || node.isSynchronized()) {
134 return super.visit(node, data);
135 }
136 // We can also skip the 'clone' method as they are generally
137 // 'useless' but as it is considered a 'good practice' to
138 // implement them anyway ( see bug 1522517)
139 if ( CLONE.equals(node.getMethodName()) && node.isPublic() &&
140 	 ! this.hasArguments(node) &&
141 	 this.isMethodType(node, OBJECT) &&
142 	 this.isMethodThrowingType(node,exceptions) )
143 {
144 	return super.visit(node,data);
145 }
146 
147 ASTBlock block = node.getBlock();
148 if (block == null) {
149 return super.visit(node, data);
150 }
151 //Only process functions with one BlockStatement
152 if (block.jjtGetNumChildren() != 1 || block.findChildrenOfType(ASTStatement.class).size() != 1)
153 return super.visit(node, data);
154 
155 ASTStatement statement = (ASTStatement) block.jjtGetChild(0).jjtGetChild(0);
156 if (statement.jjtGetChild(0).jjtGetNumChildren() == 0) {
157 return data; // skips empty return statements
158 }
159 SimpleNode statementGrandChild = (SimpleNode) statement.jjtGetChild(0).jjtGetChild(0);
160 ASTPrimaryExpression primaryExpression;
161 
162 if (statementGrandChild instanceof ASTPrimaryExpression)
163 primaryExpression = (ASTPrimaryExpression) statementGrandChild;
164 else {
165 List<ASTPrimaryExpression> primaryExpressions = findFirstDegreeChildrenOfType(statementGrandChild, ASTPrimaryExpression.class);
166 if (primaryExpressions.size() != 1)
167 return super.visit(node, data);
168 primaryExpression = primaryExpressions.get(0);
169 }
170 
171 ASTPrimaryPrefix primaryPrefix = findFirstDegreeChildrenOfType(primaryExpression, ASTPrimaryPrefix.class).get(0);
172 if (!primaryPrefix.usesSuperModifier())
173 return super.visit(node, data);
174 
175 ASTMethodDeclarator methodDeclarator = findFirstDegreeChildrenOfType(node, ASTMethodDeclarator.class).get(0);
176 if (!primaryPrefix.hasImageEqualTo(methodDeclarator.getImage()))
177 return super.visit(node, data);
178 
179 List<ASTPrimarySuffix> primarySuffixList = findFirstDegreeChildrenOfType(primaryExpression, ASTPrimarySuffix.class);
180 if (primarySuffixList.size() != 1) {
181 // extra method call on result of super method
182 return super.visit(node, data);
183 }
184 //Process arguments
185 ASTPrimarySuffix primarySuffix = primarySuffixList.get(0);
186 ASTArguments arguments = (ASTArguments) primarySuffix.jjtGetChild(0);
187 ASTFormalParameters formalParameters = (ASTFormalParameters) methodDeclarator.jjtGetChild(0);
188 if (formalParameters.jjtGetNumChildren() != arguments.jjtGetNumChildren())
189 return super.visit(node, data);
190 
191 if (!ignoreAnnotations) {
192 ASTClassOrInterfaceBodyDeclaration parent = (ASTClassOrInterfaceBodyDeclaration) node.jjtGetParent();
193 for (int i = 0; i < parent.jjtGetNumChildren(); i++) {
194 Node n = parent.jjtGetChild(i);
195 if (n instanceof ASTAnnotation) {
196 if (n.jjtGetChild(0) instanceof ASTMarkerAnnotation) {
197 // @Override is ignored
198 if ("Override".equals(((ASTName) n.jjtGetChild(0).jjtGetChild(0)).getImage())) {
199 continue;
200 }
201 }
202 return super.visit(node, data);
203 }
204 }
205 }
206 
207 if (arguments.jjtGetNumChildren() == 0) //No arguments to check
208 addViolation(data, node, getMessage());
209 else {
210 ASTArgumentList argumentList = (ASTArgumentList) arguments.jjtGetChild(0);
211 for (int i = 0; i < argumentList.jjtGetNumChildren(); i++) {
212 Node ExpressionChild = argumentList.jjtGetChild(i).jjtGetChild(0);
213 if (!(ExpressionChild instanceof ASTPrimaryExpression) || ExpressionChild.jjtGetNumChildren() != 1)
214 return super.visit(node, data); //The arguments are not simply passed through
215 
216 ASTPrimaryExpression argumentPrimaryExpression = (ASTPrimaryExpression) ExpressionChild;
217 ASTPrimaryPrefix argumentPrimaryPrefix = (ASTPrimaryPrefix) argumentPrimaryExpression.jjtGetChild(0);
218 if (argumentPrimaryPrefix.jjtGetNumChildren() == 0) {
219 return super.visit(node, data); //The arguments are not simply passed through (using "this" for instance)
220 }
221 Node argumentPrimaryPrefixChild = argumentPrimaryPrefix.jjtGetChild(0);
222 if (!(argumentPrimaryPrefixChild instanceof ASTName))
223 return super.visit(node, data); //The arguments are not simply passed through
224 
225 if (formalParameters.jjtGetNumChildren() < i + 1) {
226 return super.visit(node, data); // different number of args
227 }
228 
229 ASTName argumentName = (ASTName) argumentPrimaryPrefixChild;
230 ASTFormalParameter formalParameter = (ASTFormalParameter) formalParameters.jjtGetChild(i);
231 ASTVariableDeclaratorId variableId = findFirstDegreeChildrenOfType(formalParameter, ASTVariableDeclaratorId.class).get(0);
232 if (!argumentName.hasImageEqualTo(variableId.getImage())) {
233 return super.visit(node, data); //The arguments are not simply passed through
234 }
235 
236 }
237 addViolation(data, node, getMessage()); //All arguments are passed through directly
238 }
239 return super.visit(node, data);
240 }
241 
242 
243 
244 	public <T> List<T> findFirstDegreeChildrenOfType(SimpleNode n, Class<T> targetType) {
245 List<T> l = new ArrayList<T>();
246 lclFindChildrenOfType(n, targetType, l);
247 return l;
248 }
249 
250 private <T> void lclFindChildrenOfType(Node node, Class<T> targetType, List<T> results) {
251 if (node.getClass().equals(targetType)) {
252 results.add((T) node);
253 }
254 
255 if (node instanceof ASTClassOrInterfaceDeclaration && ((ASTClassOrInterfaceDeclaration) node).isNested()) {
256 return;
257 }
258 
259 if (node instanceof ASTClassOrInterfaceBodyDeclaration && ((ASTClassOrInterfaceBodyDeclaration) node).isAnonymousInnerClass()) {
260 return;
261 }
262 
263 for (int i = 0; i < node.jjtGetNumChildren(); i++) {
264 Node child = node.jjtGetChild(i);
265 if (child.getClass().equals(targetType)) {
266 results.add((T) child);
267 }
268 }
269 }
270 }

AltStyle によって変換されたページ (->オリジナル) /