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 net.sourceforge.pmd.AbstractRule;
7 import net.sourceforge.pmd.ast.ASTArguments;
8 import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
9 import net.sourceforge.pmd.ast.ASTCompilationUnit;
10 import net.sourceforge.pmd.ast.ASTConstructorDeclaration;
11 import net.sourceforge.pmd.ast.ASTEnumDeclaration;
12 import net.sourceforge.pmd.ast.ASTExplicitConstructorInvocation;
13 import net.sourceforge.pmd.ast.ASTLiteral;
14 import net.sourceforge.pmd.ast.ASTMethodDeclaration;
15 import net.sourceforge.pmd.ast.ASTMethodDeclarator;
16 import net.sourceforge.pmd.ast.ASTName;
17 import net.sourceforge.pmd.ast.ASTPrimaryExpression;
18 import net.sourceforge.pmd.ast.ASTPrimaryPrefix;
19 import net.sourceforge.pmd.ast.ASTPrimarySuffix;
20 import net.sourceforge.pmd.ast.AccessNode;
21 import net.sourceforge.pmd.ast.Node;
22 import net.sourceforge.pmd.ast.SimpleNode;
23
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31
32 /***
33 * Searches through all methods and constructors called from constructors. It
34 * marks as dangerous any call to overridable methods from non-private
35 * constructors. It marks as dangerous any calls to dangerous private constructors
36 * from non-private constructors.
37 *
38 * @author CL Gilbert (dnoyeb@users.sourceforge.net)
39 * @todo match parameter types. Aggressively strips off any package names. Normal
40 * compares the names as is.
41 * @todo What about interface declarations which can have internal classes
42 */
43 public final class ConstructorCallsOverridableMethod extends AbstractRule {
44 /***
45 * 2: method();
46 * ASTPrimaryPrefix
47 * ASTName image = "method"
48 * ASTPrimarySuffix
49 * *ASTArguments
50 * 3: a.method();
51 * ASTPrimaryPrefix ->
52 * ASTName image = "a.method" ???
53 * ASTPrimarySuffix -> ()
54 * ASTArguments
55 * 3: this.method();
56 * ASTPrimaryPrefix -> this image=null
57 * ASTPrimarySuffix -> method
58 * ASTPrimarySuffix -> ()
59 * ASTArguments
60 * <p/>
61 * super.method();
62 * ASTPrimaryPrefix -> image = "method"
63 * ASTPrimarySuffix -> image = null
64 * ASTArguments ->
65 * <p/>
66 * super.a.method();
67 * ASTPrimaryPrefix -> image = "a"
68 * ASTPrimarySuffix -> image = "method"
69 * ASTPrimarySuffix -> image = null
70 * ASTArguments ->
71 * <p/>
72 * <p/>
73 * 4: this.a.method();
74 * ASTPrimaryPrefix -> image = null
75 * ASTPrimarySuffix -> image = "a"
76 * ASTPrimarySuffix -> image = "method"
77 * ASTPrimarySuffix ->
78 * ASTArguments
79 * <p/>
80 * 4: ClassName.this.method();
81 * ASTPrimaryPrefix
82 * ASTName image = "ClassName"
83 * ASTPrimarySuffix -> this image=null
84 * ASTPrimarySuffix -> image = "method"
85 * ASTPrimarySuffix -> ()
86 * ASTArguments
87 * 5: ClassName.this.a.method();
88 * ASTPrimaryPrefix
89 * ASTName image = "ClassName"
90 * ASTPrimarySuffix -> this image=null
91 * ASTPrimarySuffix -> image="a"
92 * ASTPrimarySuffix -> image="method"
93 * ASTPrimarySuffix -> ()
94 * ASTArguments
95 * 5: Package.ClassName.this.method();
96 * ASTPrimaryPrefix
97 * ASTName image ="Package.ClassName"
98 * ASTPrimarySuffix -> this image=null
99 * ASTPrimarySuffix -> image="method"
100 * ASTPrimarySuffix -> ()
101 * ASTArguments
102 * 6: Package.ClassName.this.a.method();
103 * ASTPrimaryPrefix
104 * ASTName image ="Package.ClassName"
105 * ASTPrimarySuffix -> this image=null
106 * ASTPrimarySuffix -> a
107 * ASTPrimarySuffix -> method
108 * ASTPrimarySuffix -> ()
109 * ASTArguments
110 * 5: OuterClass.InnerClass.this.method();
111 * ASTPrimaryPrefix
112 * ASTName image = "OuterClass.InnerClass"
113 * ASTPrimarySuffix -> this image=null
114 * ASTPrimarySuffix -> method
115 * ASTPrimarySuffix -> ()
116 * ASTArguments
117 * 6: OuterClass.InnerClass.this.a.method();
118 * ASTPrimaryPrefix
119 * ASTName image = "OuterClass.InnerClass"
120 * ASTPrimarySuffix -> this image=null
121 * ASTPrimarySuffix -> a
122 * ASTPrimarySuffix -> method
123 * ASTPrimarySuffix -> ()
124 * ASTArguments
125 * <p/>
126 * OuterClass.InnerClass.this.a.method().method().method();
127 * ASTPrimaryPrefix
128 * ASTName image = "OuterClass.InnerClass"
129 * ASTPrimarySuffix -> this image=null
130 * ASTPrimarySuffix -> a image='a'
131 * ASTPrimarySuffix -> method image='method'
132 * ASTPrimarySuffix -> () image=null
133 * ASTArguments
134 * ASTPrimarySuffix -> method image='method'
135 * ASTPrimarySuffix -> () image=null
136 * ASTArguments
137 * ASTPrimarySuffix -> method image='method'
138 * ASTPrimarySuffix -> () image=null
139 * ASTArguments
140 * <p/>
141 * 3..n: Class.InnerClass[0].InnerClass[n].this.method();
142 * ASTPrimaryPrefix
143 * ASTName image = "Class[0]..InnerClass[n]"
144 * ASTPrimarySuffix -> image=null
145 * ASTPrimarySuffix -> method
146 * ASTPrimarySuffix -> ()
147 * ASTArguments
148 * <p/>
149 * super.aMethod();
150 * ASTPrimaryPrefix -> aMethod
151 * ASTPrimarySuffix -> ()
152 * <p/>
153 * Evaluate right to left
154 */
155 private static class MethodInvocation {
156 private String m_Name;
157 private ASTPrimaryExpression m_Ape;
158 private List m_ReferenceNames;
159 private List m_QualifierNames;
160 private int m_ArgumentSize;
161 private boolean m_Super;
162
163 private MethodInvocation(ASTPrimaryExpression ape, List qualifierNames, List referenceNames, String name, int argumentSize, boolean superCall) {
164 m_Ape = ape;
165 m_QualifierNames = qualifierNames;
166 m_ReferenceNames = referenceNames;
167 m_Name = name;
168 m_ArgumentSize = argumentSize;
169 m_Super = superCall;
170 }
171
172 public boolean isSuper() {
173 return m_Super;
174 }
175
176 public String getName() {
177 return m_Name;
178 }
179
180 public int getArgumentCount() {
181 return m_ArgumentSize;
182 }
183
184 public List getReferenceNames() {
185 return m_ReferenceNames;
186 }
187
188 public List getQualifierNames() {
189 return m_QualifierNames;
190 }
191
192 public ASTPrimaryExpression getASTPrimaryExpression() {
193 return m_Ape;
194 }
195
196 public static MethodInvocation getMethod(ASTPrimaryExpression node) {
197 MethodInvocation meth = null;
198 int i = node.jjtGetNumChildren();
199 if (i > 1) {
200
201 Node lastNode = node.jjtGetChild(i - 1);
202 if ((lastNode.jjtGetNumChildren() == 1) && (lastNode.jjtGetChild(0) instanceof ASTArguments)) {
203
204
205 List varNames = new ArrayList();
206 List packagesAndClasses = new ArrayList();
207 String methodName = null;
208 ASTArguments args = (ASTArguments) lastNode.jjtGetChild(0);
209 int numOfArguments = args.getArgumentCount();
210 boolean superFirst = false;
211 int thisIndex = -1;
212
213 FIND_SUPER_OR_THIS: {
214
215
216
217
218
219 for (int x = 0; x < i - 1; x++) {
220 Node child = node.jjtGetChild(x);
221 if (child instanceof ASTPrimarySuffix) {
222 ASTPrimarySuffix child2 = (ASTPrimarySuffix) child;
223
224
225 if (child2.getImage() == null && child2.jjtGetNumChildren() == 0) {
226 thisIndex = x;
227 break;
228 }
229
230
231
232 } else if (child instanceof ASTPrimaryPrefix) {
233 ASTPrimaryPrefix child2 = (ASTPrimaryPrefix) child;
234 if (getNameFromPrefix(child2) == null) {
235 if (child2.getImage() == null) {
236 thisIndex = x;
237 break;
238 } else {
239 superFirst = true;
240 thisIndex = x;
241
242
243 break;
244 }
245 }
246 }
247
248
249
250 }
251 }
252
253 if (thisIndex != -1) {
254
255
256 if (superFirst) {
257
258 FIRSTNODE:{
259 ASTPrimaryPrefix child = (ASTPrimaryPrefix) node.jjtGetChild(0);
260 String name = child.getImage();
261 if (i == 2) {
262 methodName = name;
263 } else {
264 varNames.add(name);
265 }
266 }
267 OTHERNODES:{
268 for (int x = 1; x < i - 1; x++) {
269 Node child = node.jjtGetChild(x);
270 ASTPrimarySuffix ps = (ASTPrimarySuffix) child;
271 if (ps.isArguments() == false) {
272 String name = ((ASTPrimarySuffix) child).getImage();
273 if (x == i - 2) {
274 methodName = name;
275 } else {
276 varNames.add(name);
277 }
278 }
279 }
280 }
281 } else {
282 FIRSTNODE:{
283 if (thisIndex == 1) {
284 ASTPrimaryPrefix child = (ASTPrimaryPrefix) node.jjtGetChild(0);
285 String toParse = getNameFromPrefix(child);
286
287 java.util.StringTokenizer st = new java.util.StringTokenizer(toParse, ".");
288 while (st.hasMoreTokens()) {
289 packagesAndClasses.add(st.nextToken());
290 }
291 }
292 }
293 OTHERNODES:{
294
295
296 for (int x = thisIndex + 1; x < i - 1; x++) {
297 ASTPrimarySuffix child = (ASTPrimarySuffix) node.jjtGetChild(x);
298 if (child.isArguments() == false) {
299 String name = child.getImage();
300
301 if (x == i - 2) {
302 methodName = name;
303 } else {
304 varNames.add(name);
305 }
306 }
307 }
308 }
309 }
310 } else {
311
312 FIRSTNODE:{
313 ASTPrimaryPrefix child = (ASTPrimaryPrefix) node.jjtGetChild(0);
314 String toParse = getNameFromPrefix(child);
315
316 java.util.StringTokenizer st = new java.util.StringTokenizer(toParse, ".");
317 while (st.hasMoreTokens()) {
318 String value = st.nextToken();
319 if (!st.hasMoreTokens()) {
320 if (i == 2) {
321 methodName = value;
322 } else {
323 varNames.add(value);
324 }
325 } else {
326 varNames.add(value);
327 }
328 }
329 }
330 OTHERNODES:{
331 for (int x = 1; x < i - 1; x++) {
332 ASTPrimarySuffix child = (ASTPrimarySuffix) node.jjtGetChild(x);
333 if (child.isArguments() == false) {
334 String name = child.getImage();
335 if (x == i - 2) {
336 methodName = name;
337 } else {
338 varNames.add(name);
339 }
340 }
341 }
342 }
343 }
344 meth = new MethodInvocation(node, packagesAndClasses, varNames, methodName, numOfArguments, superFirst);
345 }
346 }
347 return meth;
348 }
349
350 public void show() {
351 System.out.println("<MethodInvocation>");
352 List pkg = getQualifierNames();
353 System.out.println(" <Qualifiers>");
354 for (Iterator it = pkg.iterator(); it.hasNext();) {
355 String name = (String) it.next();
356 System.out.println(" " + name);
357 }
358 System.out.println(" </Qualifiers>");
359 System.out.println(" <Super>" + isSuper() + "</Super>");
360 List vars = getReferenceNames();
361 System.out.println(" <References>");
362 for (Iterator it = vars.iterator(); it.hasNext();) {
363 String name = (String) it.next();
364 System.out.println(" " + name);
365 }
366 System.out.println(" </References>");
367 System.out.println(" <Name>" + getName() + "</Name>");
368 System.out.println("</MethodInvocation>");
369 }
370 }
371
372 private static final class ConstructorInvocation {
373 private ASTExplicitConstructorInvocation m_Eci;
374 private String name;
375 private int count = 0;
376
377 public ConstructorInvocation(ASTExplicitConstructorInvocation eci) {
378 m_Eci = eci;
379 List l = new ArrayList();
380 eci.findChildrenOfType(ASTArguments.class, l);
381 if (l.size() > 0) {
382 ASTArguments aa = (ASTArguments) l.get(0);
383 count = aa.getArgumentCount();
384 }
385 name = eci.getImage();
386 }
387
388 public ASTExplicitConstructorInvocation getASTExplicitConstructorInvocation() {
389 return m_Eci;
390 }
391
392 public int getArgumentCount() {
393 return count;
394 }
395
396 public String getName() {
397 return name;
398 }
399 }
400
401 private static final class MethodHolder {
402 private ASTMethodDeclarator amd;
403 private boolean dangerous;
404 private String called;
405
406 public MethodHolder(ASTMethodDeclarator amd) {
407 this.amd = amd;
408 }
409
410 public void setCalledMethod(String name) {
411 this.called = name;
412 }
413
414 public String getCalled() {
415 return this.called;
416 }
417
418 public ASTMethodDeclarator getASTMethodDeclarator() {
419 return amd;
420 }
421
422 public boolean isDangerous() {
423 return dangerous;
424 }
425
426 public void setDangerous() {
427 dangerous = true;
428 }
429 }
430
431 private final class ConstructorHolder {
432 private ASTConstructorDeclaration m_Cd;
433 private boolean m_Dangerous;
434 private ConstructorInvocation m_Ci;
435 private boolean m_CiInitialized;
436
437 public ConstructorHolder(ASTConstructorDeclaration cd) {
438 m_Cd = cd;
439 }
440
441 public ASTConstructorDeclaration getASTConstructorDeclaration() {
442 return m_Cd;
443 }
444
445 public ConstructorInvocation getCalledConstructor() {
446 if (m_CiInitialized == false) {
447 initCI();
448 }
449 return m_Ci;
450 }
451
452 public ASTExplicitConstructorInvocation getASTExplicitConstructorInvocation() {
453 ASTExplicitConstructorInvocation eci = null;
454 if (m_CiInitialized == false) {
455 initCI();
456 }
457 if (m_Ci != null) {
458 eci = m_Ci.getASTExplicitConstructorInvocation();
459 }
460 return eci;
461 }
462
463 private void initCI() {
464 List expressions = new ArrayList();
465 m_Cd.findChildrenOfType(ASTExplicitConstructorInvocation.class, expressions);
466 if (!expressions.isEmpty()) {
467 ASTExplicitConstructorInvocation eci = (ASTExplicitConstructorInvocation) expressions.get(0);
468 m_Ci = new ConstructorInvocation(eci);
469
470 }
471 m_CiInitialized = true;
472 }
473
474 public boolean isDangerous() {
475 return m_Dangerous;
476 }
477
478 public void setDangerous(boolean dangerous) {
479 m_Dangerous = dangerous;
480 }
481 }
482
483 /***
484 * 1 package per class. holds info for evaluating a single class.
485 */
486 private static class EvalPackage {
487 public EvalPackage() {
488 }
489
490 public EvalPackage(String className) {
491 m_ClassName = className;
492 calledMethods = new ArrayList();
493 allMethodsOfClass = new HashMap();
494 calledConstructors = new ArrayList();
495 allPrivateConstructorsOfClass = new HashMap();
496 }
497
498 public String m_ClassName;
499 public List calledMethods;
500 public Map allMethodsOfClass;
501
502 public List calledConstructors;
503 public Map allPrivateConstructorsOfClass;
504 }
505
506 private static final class NullEvalPackage extends EvalPackage {
507 public NullEvalPackage() {
508 m_ClassName = "";
509 calledMethods = Collections.EMPTY_LIST;
510 allMethodsOfClass = Collections.EMPTY_MAP;
511 calledConstructors = Collections.EMPTY_LIST;
512 allPrivateConstructorsOfClass = Collections.EMPTY_MAP;
513 }
514 }
515
516 private static final NullEvalPackage nullEvalPackage = new NullEvalPackage();
517
518
519 /***
520 * 1 package per class.
521 */
522 private final List evalPackages = new ArrayList();
523
524 private EvalPackage getCurrentEvalPackage() {
525 return (EvalPackage) evalPackages.get(evalPackages.size() - 1);
526 }
527
528 /***
529 * Adds and evaluation package and makes it current
530 */
531 private void putEvalPackage(EvalPackage ep) {
532 evalPackages.add(ep);
533 }
534
535 private void removeCurrentEvalPackage() {
536 evalPackages.remove(evalPackages.size() - 1);
537 }
538
539 private void clearEvalPackages() {
540 evalPackages.clear();
541 }
542
543 /***
544 * This check must be evaluated independelty for each class. Inner classses
545 * get their own EvalPackage in order to perform independent evaluation.
546 */
547 private Object visitClassDec(ASTClassOrInterfaceDeclaration node, Object data) {
548 String className = node.getImage();
549 if (!node.isFinal() && !node.isStatic()) {
550 putEvalPackage(new EvalPackage(className));
551 } else {
552 putEvalPackage(nullEvalPackage);
553 }
554
555 super.visit((ASTClassOrInterfaceDeclaration) node, data);
556
557
558 if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {
559
560 while (evaluateDangerOfMethods(getCurrentEvalPackage().allMethodsOfClass)) {
561 }
562
563 evaluateDangerOfConstructors1(getCurrentEvalPackage().allPrivateConstructorsOfClass, getCurrentEvalPackage().allMethodsOfClass.keySet());
564 while (evaluateDangerOfConstructors2(getCurrentEvalPackage().allPrivateConstructorsOfClass)) {
565 }
566
567
568 for (Iterator it = getCurrentEvalPackage().calledMethods.iterator(); it.hasNext();) {
569 MethodInvocation meth = (MethodInvocation) it.next();
570
571 for (Iterator it2 = getCurrentEvalPackage().allMethodsOfClass.keySet().iterator(); it2.hasNext();) {
572 MethodHolder h = (MethodHolder) it2.next();
573 if (h.isDangerous()) {
574 String methName = h.getASTMethodDeclarator().getImage();
575 int count = h.getASTMethodDeclarator().getParameterCount();
576 if (meth.getName().equals(methName) && meth.getArgumentCount() == count) {
577 addViolation(data, meth.getASTPrimaryExpression(), "method '" + h.getCalled() + "'");
578 }
579 }
580 }
581 }
582
583 for (Iterator privConstIter = getCurrentEvalPackage().allPrivateConstructorsOfClass.keySet().iterator(); privConstIter.hasNext();) {
584 ConstructorHolder ch = (ConstructorHolder) privConstIter.next();
585 if (ch.isDangerous()) {
586
587 int paramCount = ch.getASTConstructorDeclaration().getParameterCount();
588 for (Iterator calledConstIter = getCurrentEvalPackage().calledConstructors.iterator(); calledConstIter.hasNext();) {
589 ConstructorInvocation ci = (ConstructorInvocation) calledConstIter.next();
590 if (ci.getArgumentCount() == paramCount) {
591
592 addViolation(data, ci.getASTExplicitConstructorInvocation(), "constructor");
593 }
594 }
595 }
596 }
597 }
598
599 removeCurrentEvalPackage();
600 return data;
601 }
602
603 /***
604 * Check the methods called on this class by each of the methods on this
605 * class. If a method calls an unsafe method, mark the calling method as
606 * unsafe. This changes the list of unsafe methods which necessitates
607 * another pass. Keep passing until you make a clean pass in which no
608 * methods are changed to unsafe.
609 * For speed it is possible to limit the number of passes.
610 * <p/>
611 * Impossible to tell type of arguments to method, so forget method matching
612 * on types. just use name and num of arguments. will be some false hits,
613 * but oh well.
614 *
615 * @todo investigate limiting the number of passes through config.
616 */
617 private boolean evaluateDangerOfMethods(Map classMethodMap) {
618
619 boolean found = false;
620 for (Iterator methodsIter = classMethodMap.keySet().iterator(); methodsIter.hasNext();) {
621 MethodHolder h = (MethodHolder) methodsIter.next();
622 List calledMeths = (List) classMethodMap.get(h);
623 for (Iterator calledMethsIter = calledMeths.iterator(); calledMethsIter.hasNext() && (h.isDangerous() == false);) {
624
625 MethodInvocation meth = (MethodInvocation) calledMethsIter.next();
626
627 for (Iterator innerMethsIter = classMethodMap.keySet().iterator(); innerMethsIter.hasNext();) {
628 MethodHolder h3 = (MethodHolder) innerMethsIter.next();
629 if (h3.isDangerous()) {
630 String matchMethodName = h3.getASTMethodDeclarator().getImage();
631 int matchMethodParamCount = h3.getASTMethodDeclarator().getParameterCount();
632
633 if (matchMethodName.equals(meth.getName()) && matchMethodParamCount == meth.getArgumentCount()) {
634 h.setDangerous();
635 h.setCalledMethod(matchMethodName);
636 found = true;
637 break;
638 }
639 }
640 }
641 }
642 }
643 return found;
644 }
645
646 /***
647 * marks constructors dangerous if they call any dangerous methods
648 * Requires only a single pass as methods are already marked
649 *
650 * @todo optimize by having methods already evaluated somehow!?
651 */
652 private void evaluateDangerOfConstructors1(Map classConstructorMap, Set evaluatedMethods) {
653
654 for (Iterator constIter = classConstructorMap.keySet().iterator(); constIter.hasNext();) {
655 ConstructorHolder ch = (ConstructorHolder) constIter.next();
656 if (!ch.isDangerous()) {
657
658 List calledMeths = (List) classConstructorMap.get(ch);
659
660 for (Iterator calledMethsIter = calledMeths.iterator(); calledMethsIter.hasNext() && !ch.isDangerous();) {
661 MethodInvocation meth = (MethodInvocation) calledMethsIter.next();
662 String methName = meth.getName();
663 int methArgCount = meth.getArgumentCount();
664
665 for (Iterator evaldMethsIter = evaluatedMethods.iterator(); evaldMethsIter.hasNext();) {
666 MethodHolder h = (MethodHolder) evaldMethsIter.next();
667 if (h.isDangerous()) {
668 String matchName = h.getASTMethodDeclarator().getImage();
669 int matchParamCount = h.getASTMethodDeclarator().getParameterCount();
670 if (methName.equals(matchName) && (methArgCount == matchParamCount)) {
671 ch.setDangerous(true);
672
673 break;
674 }
675 }
676
677 }
678 }
679 }
680 }
681 }
682
683 /***
684 * Constructor map should contain a key for each private constructor, and
685 * maps to a List which contains all called constructors of that key.
686 * marks dangerous if call dangerous private constructor
687 * we ignore all non-private constructors here. That is, the map passed in
688 * should not contain any non-private constructors.
689 * we return boolean in order to limit the number of passes through this method
690 * but it seems as if we can forgo that and just process it till its done.
691 */
692 private boolean evaluateDangerOfConstructors2(Map classConstructorMap) {
693 boolean found = false;
694
695 for (Iterator constIter = classConstructorMap.keySet().iterator(); constIter.hasNext();) {
696 ConstructorHolder ch = (ConstructorHolder) constIter.next();
697 ConstructorInvocation calledC = ch.getCalledConstructor();
698 if (calledC == null || ch.isDangerous()) {
699 continue;
700 }
701
702
703 int cCount = calledC.getArgumentCount();
704 for (Iterator innerConstIter = classConstructorMap.keySet().iterator(); innerConstIter.hasNext() && !ch.isDangerous();) {
705 ConstructorHolder h2 = (ConstructorHolder) innerConstIter.next();
706 if (h2.isDangerous()) {
707 int matchConstArgCount = h2.getASTConstructorDeclaration().getParameterCount();
708 if (matchConstArgCount == cCount) {
709 ch.setDangerous(true);
710 found = true;
711
712 }
713 }
714 }
715 }
716 return found;
717 }
718
719 public Object visit(ASTCompilationUnit node, Object data) {
720 clearEvalPackages();
721 return super.visit(node, data);
722 }
723
724 public Object visit(ASTEnumDeclaration node, Object data) {
725
726 return data;
727 }
728
729 /***
730 * This check must be evaluated independelty for each class. Inner classses
731 * get their own EvalPackage in order to perform independent evaluation.
732 */
733 public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
734 if (!node.isInterface()) {
735 return visitClassDec(node, data);
736 } else {
737 putEvalPackage(nullEvalPackage);
738 Object o = super.visit(node, data);
739 removeCurrentEvalPackage();
740 return o;
741 }
742 }
743
744
745 /***
746 * Non-private constructor's methods are added to a list for later safety
747 * evaluation. Non-private constructor's calls on private constructors
748 * are added to a list for later safety evaluation. Private constructors
749 * are added to a list so their safety to be called can be later evaluated.
750 * <p/>
751 * Note: We are not checking private constructor's calls on non-private
752 * constructors because all non-private constructors will be evaluated for
753 * safety anyway. This means we wont flag a private constructor as unsafe
754 * just because it calls an unsafe public constructor. We want to show only
755 * 1 instance of an error, and this would be 2 instances of the same error.
756 *
757 * @todo eliminate the redundency
758 */
759 public Object visit(ASTConstructorDeclaration node, Object data) {
760 if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {
761 List calledMethodsOfConstructor = new ArrayList();
762 ConstructorHolder ch = new ConstructorHolder(node);
763 addCalledMethodsOfNode(node, calledMethodsOfConstructor, getCurrentEvalPackage().m_ClassName);
764 if (!node.isPrivate()) {
765
766 getCurrentEvalPackage().calledMethods.addAll(calledMethodsOfConstructor);
767
768
769
770 ASTExplicitConstructorInvocation eci = ch.getASTExplicitConstructorInvocation();
771 if (eci != null && eci.isThis()) {
772 getCurrentEvalPackage().calledConstructors.add(ch.getCalledConstructor());
773 }
774 } else {
775
776
777 getCurrentEvalPackage().allPrivateConstructorsOfClass.put(ch, calledMethodsOfConstructor);
778 }
779 }
780 return super.visit(node, data);
781 }
782
783 /***
784 * Create a MethodHolder to hold the method.
785 * Store the MethodHolder in the Map as the key
786 * Store each method called by the current method as a List in the Map as the Object
787 */
788 public Object visit(ASTMethodDeclarator node, Object data) {
789 if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {
790 AccessNode parent = (AccessNode) node.jjtGetParent();
791 MethodHolder h = new MethodHolder(node);
792 if (!parent.isAbstract() && !parent.isPrivate() && !parent.isStatic() && !parent.isFinal()) {
793 h.setDangerous();
794 ASTMethodDeclaration decl = (ASTMethodDeclaration) node.getFirstParentOfType(ASTMethodDeclaration.class);
795 h.setCalledMethod(decl.getMethodName());
796 }
797 List l = new ArrayList();
798 addCalledMethodsOfNode((SimpleNode) parent, l, getCurrentEvalPackage().m_ClassName);
799 getCurrentEvalPackage().allMethodsOfClass.put(h, l);
800 }
801 return super.visit(node, data);
802 }
803
804
805 private static void addCalledMethodsOfNode(AccessNode node, List calledMethods, String className) {
806 List expressions = new ArrayList();
807 node.findChildrenOfType(ASTPrimaryExpression.class, expressions, false);
808 addCalledMethodsOfNodeImpl(expressions, calledMethods, className);
809 }
810
811 /***
812 * Adds all methods called on this instance from within this Node.
813 */
814 private static void addCalledMethodsOfNode(SimpleNode node, List calledMethods, String className) {
815 List expressions = new ArrayList();
816 node.findChildrenOfType(ASTPrimaryExpression.class, expressions);
817 addCalledMethodsOfNodeImpl(expressions, calledMethods, className);
818 }
819
820 private static void addCalledMethodsOfNodeImpl(List expressions, List calledMethods, String className) {
821 for (Iterator it = expressions.iterator(); it.hasNext();) {
822 ASTPrimaryExpression ape = (ASTPrimaryExpression) it.next();
823 MethodInvocation meth = findMethod(ape, className);
824 if (meth != null) {
825
826 calledMethods.add(meth);
827 }
828 }
829 }
830
831 /***
832 * @return A method call on the class passed in, or null if no method call
833 * is found.
834 * @todo Need a better way to match the class and package name to the actual
835 * method being called.
836 */
837 private static MethodInvocation findMethod(ASTPrimaryExpression node, String className) {
838 if (node.jjtGetNumChildren() > 0
839 && node.jjtGetChild(0).jjtGetNumChildren() > 0
840 && node.jjtGetChild(0).jjtGetChild(0) instanceof ASTLiteral) {
841 return null;
842 }
843 MethodInvocation meth = MethodInvocation.getMethod(node);
844 boolean found = false;
845
846
847
848 if (meth != null) {
849
850 if ((meth.getReferenceNames().size() == 0) && !meth.isSuper()) {
851
852
853 List packClass = meth.getQualifierNames();
854 if (packClass.size() > 0) {
855 for (Iterator it = packClass.iterator(); it.hasNext();) {
856 String name = (String) it.next();
857 if (name.equals(className)) {
858 found = true;
859 break;
860 }
861 }
862 } else {
863 found = true;
864 }
865 }
866 }
867
868 return found ? meth : null;
869 }
870
871 /***
872 * ASTPrimaryPrefix has name in child node of ASTName
873 */
874 private static String getNameFromPrefix(ASTPrimaryPrefix node) {
875 String name = null;
876
877 if (node.jjtGetNumChildren() == 1) {
878 Node nnode = node.jjtGetChild(0);
879 if (nnode instanceof ASTName) {
880 name = ((ASTName) nnode).getImage();
881 }
882 }
883 return name;
884 }
885
886 }