1
2
3
4 package net.sourceforge.pmd.lang.java.rule.unnecessary;
5
6 import java.util.ArrayList;
7 import java.util.List;
8
9 import net.sourceforge.pmd.lang.ast.Node;
10 import net.sourceforge.pmd.lang.java.ast.ASTAnnotation;
11 import net.sourceforge.pmd.lang.java.ast.ASTArgumentList;
12 import net.sourceforge.pmd.lang.java.ast.ASTArguments;
13 import net.sourceforge.pmd.lang.java.ast.ASTBlock;
14 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration;
15 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
16 import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
17 import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
18 import net.sourceforge.pmd.lang.java.ast.ASTFormalParameters;
19 import net.sourceforge.pmd.lang.java.ast.ASTImplementsList;
20 import net.sourceforge.pmd.lang.java.ast.ASTMarkerAnnotation;
21 import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
22 import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator;
23 import net.sourceforge.pmd.lang.java.ast.ASTName;
24 import net.sourceforge.pmd.lang.java.ast.ASTNameList;
25 import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
26 import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
27 import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix;
28 import net.sourceforge.pmd.lang.java.ast.ASTResultType;
29 import net.sourceforge.pmd.lang.java.ast.ASTStatement;
30 import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
31 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
32 import net.sourceforge.pmd.lang.rule.properties.BooleanProperty;
33
34
35
36
37 public class UselessOverridingMethodRule extends AbstractJavaRule {
38 private final List<String> exceptions;
39 private boolean ignoreAnnotations;
40 private static final String CLONE = "clone";
41 private static final String OBJECT = "Object";
42
43 private static final BooleanProperty IGNORE_ANNOTATIONS_DESCRIPTOR = new BooleanProperty(
44 "ignoreAnnotations", "Ignore annotations", false, 1.0f);
45
46 public UselessOverridingMethodRule() {
47 definePropertyDescriptor(IGNORE_ANNOTATIONS_DESCRIPTOR);
48
49 exceptions = new ArrayList<String>(1);
50 exceptions.add("CloneNotSupportedException");
51 }
52
53 @Override
54 public Object visit(ASTCompilationUnit node, Object data) {
55 init();
56 return super.visit(node, data);
57 }
58
59 private void init() {
60 ignoreAnnotations = getProperty(IGNORE_ANNOTATIONS_DESCRIPTOR);
61 }
62
63 @Override
64 public Object visit(ASTImplementsList clz, Object data) {
65 return super.visit(clz, data);
66 }
67
68 @Override
69 public Object visit(ASTClassOrInterfaceDeclaration clz, Object data) {
70 if (clz.isInterface()) {
71 return data;
72 }
73 return super.visit(clz, data);
74 }
75
76
77 private boolean isMethodType(ASTMethodDeclaration node, String methodType) {
78 boolean result = false;
79 ASTResultType type = node.getResultType();
80 if (type != null) {
81 result = type.hasDescendantMatchingXPath("./Type/ReferenceType/ClassOrInterfaceType[@Image = '"
82 + methodType + "']");
83 }
84 return result;
85 }
86
87
88 private boolean isMethodThrowingType(ASTMethodDeclaration node, List<String> exceptedExceptions) {
89 boolean result = false;
90 ASTNameList thrownsExceptions = node.getFirstChildOfType(ASTNameList.class);
91 if (thrownsExceptions != null) {
92 List<ASTName> names = thrownsExceptions.findChildrenOfType(ASTName.class);
93 for (ASTName name : names) {
94 for (String exceptedException : exceptedExceptions) {
95 if (exceptedException.equals(name.getImage())) {
96 result = true;
97 }
98 }
99 }
100 }
101 return result;
102 }
103
104 private boolean hasArguments(ASTMethodDeclaration node) {
105 return node.hasDescendantMatchingXPath("./MethodDeclarator/FormalParameters/*");
106 }
107
108 @Override
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() && !this.hasArguments(node)
120 && this.isMethodType(node, OBJECT) && this.isMethodThrowingType(node, exceptions)) {
121 return super.visit(node, data);
122 }
123
124 ASTBlock block = node.getBlock();
125 if (block == null) {
126 return super.visit(node, data);
127 }
128
129 if (block.jjtGetNumChildren() != 1 || block.findDescendantsOfType(ASTStatement.class).size() != 1) {
130 return super.visit(node, data);
131 }
132
133 ASTStatement statement = (ASTStatement) block.jjtGetChild(0).jjtGetChild(0);
134 if (statement.jjtGetChild(0).jjtGetNumChildren() == 0) {
135 return data;
136 }
137 Node statementGrandChild = statement.jjtGetChild(0).jjtGetChild(0);
138 ASTPrimaryExpression primaryExpression;
139
140 if (statementGrandChild instanceof ASTPrimaryExpression) {
141 primaryExpression = (ASTPrimaryExpression) statementGrandChild;
142 } else {
143 List<ASTPrimaryExpression> primaryExpressions = findFirstDegreeChildrenOfType(statementGrandChild,
144 ASTPrimaryExpression.class);
145 if (primaryExpressions.size() != 1) {
146 return super.visit(node, data);
147 }
148 primaryExpression = primaryExpressions.get(0);
149 }
150
151 ASTPrimaryPrefix primaryPrefix = findFirstDegreeChildrenOfType(primaryExpression, ASTPrimaryPrefix.class)
152 .get(0);
153 if (!primaryPrefix.usesSuperModifier()) {
154 return super.visit(node, data);
155 }
156
157 List<ASTPrimarySuffix> primarySuffixList = findFirstDegreeChildrenOfType(primaryExpression,
158 ASTPrimarySuffix.class);
159 if (primarySuffixList.size() != 2) {
160
161 return super.visit(node, data);
162 }
163
164 ASTMethodDeclarator methodDeclarator = findFirstDegreeChildrenOfType(node, ASTMethodDeclarator.class).get(0);
165 ASTPrimarySuffix primarySuffix = primarySuffixList.get(0);
166 if (!primarySuffix.hasImageEqualTo(methodDeclarator.getImage())) {
167 return super.visit(node, data);
168 }
169
170 primarySuffix = primarySuffixList.get(1);
171 ASTArguments arguments = (ASTArguments) primarySuffix.jjtGetChild(0);
172 ASTFormalParameters formalParameters = (ASTFormalParameters) methodDeclarator.jjtGetChild(0);
173 if (formalParameters.jjtGetNumChildren() != arguments.jjtGetNumChildren()) {
174 return super.visit(node, data);
175 }
176
177 if (!ignoreAnnotations) {
178 ASTClassOrInterfaceBodyDeclaration parent = (ASTClassOrInterfaceBodyDeclaration) node.jjtGetParent();
179 for (int i = 0; i < parent.jjtGetNumChildren(); i++) {
180 Node n = parent.jjtGetChild(i);
181 if (n instanceof ASTAnnotation) {
182 if (n.jjtGetChild(0) instanceof ASTMarkerAnnotation) {
183
184 if ("Override".equals(((ASTName) n.jjtGetChild(0).jjtGetChild(0)).getImage())) {
185 continue;
186 }
187 }
188 return super.visit(node, data);
189 }
190 }
191 }
192
193 if (arguments.jjtGetNumChildren() == 0) {
194 addViolation(data, node, getMessage());
195 } else {
196 ASTArgumentList argumentList = (ASTArgumentList) arguments.jjtGetChild(0);
197 for (int i = 0; i < argumentList.jjtGetNumChildren(); i++) {
198 Node expressionChild = argumentList.jjtGetChild(i).jjtGetChild(0);
199 if (!(expressionChild instanceof ASTPrimaryExpression) || expressionChild.jjtGetNumChildren() != 1) {
200 return super.visit(node, data);
201 }
202
203 ASTPrimaryExpression argumentPrimaryExpression = (ASTPrimaryExpression) expressionChild;
204 ASTPrimaryPrefix argumentPrimaryPrefix = (ASTPrimaryPrefix) argumentPrimaryExpression.jjtGetChild(0);
205 if (argumentPrimaryPrefix.jjtGetNumChildren() == 0) {
206 return super.visit(node, data);
207 }
208 Node argumentPrimaryPrefixChild = argumentPrimaryPrefix.jjtGetChild(0);
209 if (!(argumentPrimaryPrefixChild instanceof ASTName)) {
210 return super.visit(node, data);
211 }
212
213 if (formalParameters.jjtGetNumChildren() < i + 1) {
214 return super.visit(node, data);
215 }
216
217 ASTName argumentName = (ASTName) argumentPrimaryPrefixChild;
218 ASTFormalParameter formalParameter = (ASTFormalParameter) formalParameters.jjtGetChild(i);
219 ASTVariableDeclaratorId variableId = findFirstDegreeChildrenOfType(formalParameter,
220 ASTVariableDeclaratorId.class).get(0);
221 if (!argumentName.hasImageEqualTo(variableId.getImage())) {
222 return super.visit(node, data);
223 }
224
225 }
226 addViolation(data, node, getMessage());
227 }
228 return super.visit(node, data);
229 }
230
231 public <T> List<T> findFirstDegreeChildrenOfType(Node n, Class<T> targetType) {
232 List<T> l = new ArrayList<T>();
233 lclFindChildrenOfType(n, targetType, l);
234 return l;
235 }
236
237 private <T> void lclFindChildrenOfType(Node node, Class<T> targetType, List<T> results) {
238 if (node.getClass().equals(targetType)) {
239 results.add((T) node);
240 }
241
242 if (node instanceof ASTClassOrInterfaceDeclaration && ((ASTClassOrInterfaceDeclaration) node).isNested()) {
243 return;
244 }
245
246 if (node instanceof ASTClassOrInterfaceBodyDeclaration
247 && ((ASTClassOrInterfaceBodyDeclaration) node).isAnonymousInnerClass()) {
248 return;
249 }
250
251 for (int i = 0; i < node.jjtGetNumChildren(); i++) {
252 Node child = node.jjtGetChild(i);
253 if (child.getClass().equals(targetType)) {
254 results.add((T) child);
255 }
256 }
257 }
258 }