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
9 import net.sourceforge.pmd.AbstractRule;
10 import net.sourceforge.pmd.ast.ASTArgumentList;
11 import net.sourceforge.pmd.ast.ASTArguments;
12 import net.sourceforge.pmd.ast.ASTBlock;
13 import net.sourceforge.pmd.ast.ASTClassOrInterfaceBodyDeclaration;
14 import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
15 import net.sourceforge.pmd.ast.ASTFormalParameter;
16 import net.sourceforge.pmd.ast.ASTFormalParameters;
17 import net.sourceforge.pmd.ast.ASTImplementsList;
18 import net.sourceforge.pmd.ast.ASTMethodDeclaration;
19 import net.sourceforge.pmd.ast.ASTMethodDeclarator;
20 import net.sourceforge.pmd.ast.ASTName;
21 import net.sourceforge.pmd.ast.ASTNameList;
22 import net.sourceforge.pmd.ast.ASTPrimaryExpression;
23 import net.sourceforge.pmd.ast.ASTPrimaryPrefix;
24 import net.sourceforge.pmd.ast.ASTPrimarySuffix;
25 import net.sourceforge.pmd.ast.ASTResultType;
26 import net.sourceforge.pmd.ast.ASTStatement;
27 import net.sourceforge.pmd.ast.ASTVariableDeclaratorId;
28 import net.sourceforge.pmd.ast.Node;
29 import net.sourceforge.pmd.ast.SimpleNode;
30
31 import org.jaxen.JaxenException;
32
33 /**
34 * @author Romain Pelisse, bugfix for [ 1522517 ] False +: UselessOverridingMethod
35 */
36 public class UselessOverridingMethod extends AbstractRule {
37 private List<String> exceptions;
38 private static final String CLONE = "clone";
39 private static final String OBJECT = "Object";
40
41 public UselessOverridingMethod()
42 {
43 exceptions = new ArrayList<String>(1);
44 exceptions.add("CloneNotSupportedException");
45 }
46
47 public Object visit(ASTImplementsList clz, Object data)
48 {
49 return super.visit(clz,data);
50 }
51
52 public Object visit(ASTClassOrInterfaceDeclaration clz, Object data) {
53 if (clz.isInterface()) {
54 return data;
55 }
56 return super.visit(clz, data);
57 }
58
59
60 private boolean isMethodType(ASTMethodDeclaration node,String methodType)
61 {
62 boolean result = false;
63 ASTResultType type = node.getResultType();
64 if ( type != null ) {
65 List results = null;
66 try {
67 results = type.findChildNodesWithXPath("./Type/ReferenceType/ClassOrInterfaceType[@Image = '" + methodType + "']");
68 }
69 catch (JaxenException e) {
70 e.printStackTrace();
71 }
72 if ( results != null && results.size() > 0 ) {
73 result = true;
74 }
75 }
76 return result;
77 }
78
79
80 private boolean isMethodThrowingType(ASTMethodDeclaration node, List<String> exceptedExceptions) {
81 boolean result = false;
82 ASTNameList thrownsExceptions = node.getFirstChildOfType(ASTNameList.class);
83 if ( thrownsExceptions != null ) {
84 List<ASTName> names = thrownsExceptions.findChildrenOfType(ASTName.class);
85 for ( ASTName name : names ) {
86 for ( String exceptedException : exceptedExceptions) {
87 if ( exceptedException.equals(name.getImage()) )
88 result = true;
89 }
90 }
91 }
92 return result;
93 }
94
95 private boolean hasArguments(ASTMethodDeclaration node) {
96 boolean result = false;
97 try
98 {
99 List parameters = node.findChildNodesWithXPath("./MethodDeclarator/FormalParameters/*");
100 if ( parameters != null && parameters.size() < 0 ) {
101 result = true;
102 }
103 } catch (JaxenException e) {
104 e.printStackTrace();
105 }
106 return result;
107 }
108
109 public Object visit(ASTMethodDeclaration node, Object data) {
110
111
112
113 if (node.isAbstract() || node.isFinal() || node.isNative() || node.isSynchronized()) {
114 return super.visit(node, data);
115 }
116
117
118
119 if ( CLONE.equals(node.getMethodName()) && node.isPublic() &&
120 ! this.hasArguments(node) &&
121 this.isMethodType(node, OBJECT) &&
122 this.isMethodThrowingType(node,exceptions) )
123 {
124 return super.visit(node,data);
125 }
126
127 ASTBlock block = node.getBlock();
128 if (block == null) {
129 return super.visit(node, data);
130 }
131
132 if (block.jjtGetNumChildren() != 1 || block.findChildrenOfType(ASTStatement.class).size() != 1)
133 return super.visit(node, data);
134
135 ASTStatement statement = (ASTStatement) block.jjtGetChild(0).jjtGetChild(0);
136 if (statement.jjtGetChild(0).jjtGetNumChildren() == 0) {
137 return data;
138 }
139 SimpleNode statementGrandChild = (SimpleNode) statement.jjtGetChild(0).jjtGetChild(0);
140 ASTPrimaryExpression primaryExpression;
141
142 if (statementGrandChild instanceof ASTPrimaryExpression)
143 primaryExpression = (ASTPrimaryExpression) statementGrandChild;
144 else {
145 List<ASTPrimaryExpression> primaryExpressions = findFirstDegreeChildrenOfType(statementGrandChild, ASTPrimaryExpression.class);
146 if (primaryExpressions.size() != 1)
147 return super.visit(node, data);
148 primaryExpression = primaryExpressions.get(0);
149 }
150
151 ASTPrimaryPrefix primaryPrefix = findFirstDegreeChildrenOfType(primaryExpression, ASTPrimaryPrefix.class).get(0);
152 if (!primaryPrefix.usesSuperModifier())
153 return super.visit(node, data);
154
155 ASTMethodDeclarator methodDeclarator = findFirstDegreeChildrenOfType(node, ASTMethodDeclarator.class).get(0);
156 if (!primaryPrefix.hasImageEqualTo(methodDeclarator.getImage()))
157 return super.visit(node, data);
158
159
160 ASTPrimarySuffix primarySuffix = findFirstDegreeChildrenOfType(primaryExpression, ASTPrimarySuffix.class).get(0);
161 ASTArguments arguments = (ASTArguments) primarySuffix.jjtGetChild(0);
162 ASTFormalParameters formalParameters = (ASTFormalParameters) methodDeclarator.jjtGetChild(0);
163 if (formalParameters.jjtGetNumChildren() != arguments.jjtGetNumChildren())
164 return super.visit(node, data);
165
166 if (arguments.jjtGetNumChildren() == 0)
167 addViolation(data, node, getMessage());
168 else {
169 ASTArgumentList argumentList = (ASTArgumentList) arguments.jjtGetChild(0);
170 for (int i = 0; i < argumentList.jjtGetNumChildren(); i++) {
171 Node ExpressionChild = argumentList.jjtGetChild(i).jjtGetChild(0);
172 if (!(ExpressionChild instanceof ASTPrimaryExpression) || ExpressionChild.jjtGetNumChildren() != 1)
173 return super.visit(node, data);
174
175 ASTPrimaryExpression argumentPrimaryExpression = (ASTPrimaryExpression) ExpressionChild;
176 ASTPrimaryPrefix argumentPrimaryPrefix = (ASTPrimaryPrefix) argumentPrimaryExpression.jjtGetChild(0);
177 if (argumentPrimaryPrefix.jjtGetNumChildren() == 0) {
178 return super.visit(node, data);
179 }
180 Node argumentPrimaryPrefixChild = argumentPrimaryPrefix.jjtGetChild(0);
181 if (!(argumentPrimaryPrefixChild instanceof ASTName))
182 return super.visit(node, data);
183
184 if (formalParameters.jjtGetNumChildren() < i + 1) {
185 return super.visit(node, data);
186 }
187
188 ASTName argumentName = (ASTName) argumentPrimaryPrefixChild;
189 ASTFormalParameter formalParameter = (ASTFormalParameter) formalParameters.jjtGetChild(i);
190 ASTVariableDeclaratorId variableId = findFirstDegreeChildrenOfType(formalParameter, ASTVariableDeclaratorId.class).get(0);
191 if (!argumentName.hasImageEqualTo(variableId.getImage())) {
192 return super.visit(node, data);
193 }
194
195 }
196 addViolation(data, node, getMessage());
197 }
198 return super.visit(node, data);
199 }
200
201
202
203 public <T> List<T> findFirstDegreeChildrenOfType(SimpleNode n, Class<T> targetType) {
204 List<T> l = new ArrayList<T>();
205 lclFindChildrenOfType(n, targetType, l);
206 return l;
207 }
208
209 private <T> void lclFindChildrenOfType(Node node, Class<T> targetType, List<T> results) {
210 if (node.getClass().equals(targetType)) {
211 results.add((T) node);
212 }
213
214 if (node instanceof ASTClassOrInterfaceDeclaration && ((ASTClassOrInterfaceDeclaration) node).isNested()) {
215 return;
216 }
217
218 if (node instanceof ASTClassOrInterfaceBodyDeclaration && ((ASTClassOrInterfaceBodyDeclaration) node).isAnonymousInnerClass()) {
219 return;
220 }
221
222 for (int i = 0; i < node.jjtGetNumChildren(); i++) {
223 Node child = node.jjtGetChild(i);
224 if (child.getClass().equals(targetType)) {
225 results.add((T) child);
226 }
227 }
228 }
229 }