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 evaluateDangerOfConstructors1(getCurrentEvalPackage().allPrivateConstructorsOfClass, getCurrentEvalPackage().allMethodsOfClass.keySet());
563 while (evaluateDangerOfConstructors2(getCurrentEvalPackage().allPrivateConstructorsOfClass)) {}
564
565
566 for (Iterator it = getCurrentEvalPackage().calledMethods.iterator(); it.hasNext();) {
567 MethodInvocation meth = (MethodInvocation) it.next();
568
569 for (Iterator it2 = getCurrentEvalPackage().allMethodsOfClass.keySet().iterator(); it2.hasNext();) {
570 MethodHolder h = (MethodHolder) it2.next();
571 if (h.isDangerous()) {
572 String methName = h.getASTMethodDeclarator().getImage();
573 int count = h.getASTMethodDeclarator().getParameterCount();
574 if (meth.getName().equals(methName) && meth.getArgumentCount() == count) {
575 addViolation( data, meth.getASTPrimaryExpression(), "method '" + h.getCalled() + "'");
576 }
577 }
578 }
579 }
580
581 for (Iterator privConstIter = getCurrentEvalPackage().allPrivateConstructorsOfClass.keySet().iterator(); privConstIter.hasNext();) {
582 ConstructorHolder ch = (ConstructorHolder) privConstIter.next();
583 if (ch.isDangerous()) {
584
585 int paramCount = ch.getASTConstructorDeclaration().getParameterCount();
586 for (Iterator calledConstIter = getCurrentEvalPackage().calledConstructors.iterator(); calledConstIter.hasNext();) {
587 ConstructorInvocation ci = (ConstructorInvocation) calledConstIter.next();
588 if (ci.getArgumentCount() == paramCount) {
589
590 addViolation(data, ci.getASTExplicitConstructorInvocation(), "constructor");
591 }
592 }
593 }
594 }
595 }
596
597 removeCurrentEvalPackage();
598 return data;
599 }
600 /***
601 * Check the methods called on this class by each of the methods on this
602 * class. If a method calls an unsafe method, mark the calling method as
603 * unsafe. This changes the list of unsafe methods which necessitates
604 * another pass. Keep passing until you make a clean pass in which no
605 * methods are changed to unsafe.
606 * For speed it is possible to limit the number of passes.
607 * <p/>
608 * Impossible to tell type of arguments to method, so forget method matching
609 * on types. just use name and num of arguments. will be some false hits,
610 * but oh well.
611 *
612 * @todo investigate limiting the number of passes through config.
613 */
614 private boolean evaluateDangerOfMethods(Map classMethodMap) {
615
616 boolean found = false;
617 for (Iterator methodsIter = classMethodMap.keySet().iterator(); methodsIter.hasNext();) {
618 MethodHolder h = (MethodHolder) methodsIter.next();
619 List calledMeths = (List) classMethodMap.get(h);
620 for (Iterator calledMethsIter = calledMeths.iterator(); calledMethsIter.hasNext() && (h.isDangerous() == false);) {
621
622 MethodInvocation meth = (MethodInvocation) calledMethsIter.next();
623
624 for (Iterator innerMethsIter = classMethodMap.keySet().iterator(); innerMethsIter.hasNext();) {
625 MethodHolder h3 = (MethodHolder) innerMethsIter.next();
626 if (h3.isDangerous()) {
627 String matchMethodName = h3.getASTMethodDeclarator().getImage();
628 int matchMethodParamCount = h3.getASTMethodDeclarator().getParameterCount();
629
630 if (matchMethodName.equals(meth.getName()) && matchMethodParamCount == meth.getArgumentCount()) {
631 h.setDangerous();
632 h.setCalledMethod(matchMethodName);
633 found = true;
634 break;
635 }
636 }
637 }
638 }
639 }
640 return found;
641 }
642
643 /***
644 * marks constructors dangerous if they call any dangerous methods
645 * Requires only a single pass as methods are already marked
646 *
647 * @todo optimize by having methods already evaluated somehow!?
648 */
649 private void evaluateDangerOfConstructors1(Map classConstructorMap, Set evaluatedMethods) {
650
651 for (Iterator constIter = classConstructorMap.keySet().iterator(); constIter.hasNext();) {
652 ConstructorHolder ch = (ConstructorHolder) constIter.next();
653 if (!ch.isDangerous()) {
654
655 List calledMeths = (List) classConstructorMap.get(ch);
656
657 for (Iterator calledMethsIter = calledMeths.iterator(); calledMethsIter.hasNext() && !ch.isDangerous();) {
658 MethodInvocation meth = (MethodInvocation) calledMethsIter.next();
659 String methName = meth.getName();
660 int methArgCount = meth.getArgumentCount();
661
662 for (Iterator evaldMethsIter = evaluatedMethods.iterator(); evaldMethsIter.hasNext();) {
663 MethodHolder h = (MethodHolder) evaldMethsIter.next();
664 if (h.isDangerous()) {
665 String matchName = h.getASTMethodDeclarator().getImage();
666 int matchParamCount = h.getASTMethodDeclarator().getParameterCount();
667 if (methName.equals(matchName) && (methArgCount == matchParamCount)) {
668 ch.setDangerous(true);
669
670 break;
671 }
672 }
673
674 }
675 }
676 }
677 }
678 }
679
680 /***
681 * Constructor map should contain a key for each private constructor, and
682 * maps to a List which contains all called constructors of that key.
683 * marks dangerous if call dangerous private constructor
684 * we ignore all non-private constructors here. That is, the map passed in
685 * should not contain any non-private constructors.
686 * we return boolean in order to limit the number of passes through this method
687 * but it seems as if we can forgo that and just process it till its done.
688 */
689 private boolean evaluateDangerOfConstructors2(Map classConstructorMap) {
690 boolean found = false;
691
692 for (Iterator constIter = classConstructorMap.keySet().iterator(); constIter.hasNext();) {
693 ConstructorHolder ch = (ConstructorHolder) constIter.next();
694 ConstructorInvocation calledC = ch.getCalledConstructor();
695 if (calledC == null || ch.isDangerous()) {
696 continue;
697 }
698
699
700 int cCount = calledC.getArgumentCount();
701 for (Iterator innerConstIter = classConstructorMap.keySet().iterator(); innerConstIter.hasNext() && !ch.isDangerous();) {
702 ConstructorHolder h2 = (ConstructorHolder) innerConstIter.next();
703 if (h2.isDangerous()) {
704 int matchConstArgCount = h2.getASTConstructorDeclaration().getParameterCount();
705 if (matchConstArgCount == cCount) {
706 ch.setDangerous(true);
707 found = true;
708
709 }
710 }
711 }
712 }
713 return found;
714 }
715
716 public Object visit(ASTCompilationUnit node, Object data) {
717 clearEvalPackages();
718 return super.visit(node, data);
719 }
720
721 public Object visit(ASTEnumDeclaration node, Object data) {
722
723 return data;
724 }
725
726 /***
727 * This check must be evaluated independelty for each class. Inner classses
728 * get their own EvalPackage in order to perform independent evaluation.
729 */
730 public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
731 if (!node.isInterface()) {
732 return visitClassDec(node, data);
733 } else {
734 putEvalPackage(nullEvalPackage);
735 Object o = super.visit(node, data);
736 removeCurrentEvalPackage();
737 return o;
738 }
739 }
740
741
742 /***
743 * Non-private constructor's methods are added to a list for later safety
744 * evaluation. Non-private constructor's calls on private constructors
745 * are added to a list for later safety evaluation. Private constructors
746 * are added to a list so their safety to be called can be later evaluated.
747 * <p/>
748 * Note: We are not checking private constructor's calls on non-private
749 * constructors because all non-private constructors will be evaluated for
750 * safety anyway. This means we wont flag a private constructor as unsafe
751 * just because it calls an unsafe public constructor. We want to show only
752 * 1 instance of an error, and this would be 2 instances of the same error.
753 *
754 * @todo eliminate the redundency
755 */
756 public Object visit(ASTConstructorDeclaration node, Object data) {
757 if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {
758 List calledMethodsOfConstructor = new ArrayList();
759 ConstructorHolder ch = new ConstructorHolder(node);
760 addCalledMethodsOfNode(node, calledMethodsOfConstructor, getCurrentEvalPackage().m_ClassName);
761 if (!node.isPrivate()) {
762
763 getCurrentEvalPackage().calledMethods.addAll(calledMethodsOfConstructor);
764
765
766
767 ASTExplicitConstructorInvocation eci = ch.getASTExplicitConstructorInvocation();
768 if (eci != null && eci.isThis()) {
769 getCurrentEvalPackage().calledConstructors.add(ch.getCalledConstructor());
770 }
771 } else {
772
773
774 getCurrentEvalPackage().allPrivateConstructorsOfClass.put(ch, calledMethodsOfConstructor);
775 }
776 }
777 return super.visit(node, data);
778 }
779
780 /***
781 * Create a MethodHolder to hold the method.
782 * Store the MethodHolder in the Map as the key
783 * Store each method called by the current method as a List in the Map as the Object
784 */
785 public Object visit(ASTMethodDeclarator node, Object data) {
786 if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {
787 AccessNode parent = (AccessNode) node.jjtGetParent();
788 MethodHolder h = new MethodHolder(node);
789 if (!parent.isAbstract() && !parent.isPrivate() && !parent.isStatic() && !parent.isFinal()) {
790 h.setDangerous();
791 ASTMethodDeclaration decl = (ASTMethodDeclaration)node.getFirstParentOfType(ASTMethodDeclaration.class);
792 h.setCalledMethod(decl.getMethodName());
793 }
794 List l = new ArrayList();
795 addCalledMethodsOfNode((SimpleNode) parent, l, getCurrentEvalPackage().m_ClassName);
796 getCurrentEvalPackage().allMethodsOfClass.put(h, l);
797 }
798 return super.visit(node, data);
799 }
800
801
802
803 private static void addCalledMethodsOfNode(AccessNode node, List calledMethods, String className) {
804 List expressions = new ArrayList();
805 node.findChildrenOfType(ASTPrimaryExpression.class, expressions, false);
806 addCalledMethodsOfNodeImpl(expressions, calledMethods, className);
807 }
808
809 /***
810 * Adds all methods called on this instance from within this Node.
811 */
812 private static void addCalledMethodsOfNode(SimpleNode node, List calledMethods, String className) {
813 List expressions = new ArrayList();
814 node.findChildrenOfType(ASTPrimaryExpression.class, expressions);
815 addCalledMethodsOfNodeImpl(expressions, calledMethods, className);
816 }
817
818 private static void addCalledMethodsOfNodeImpl(List expressions, List calledMethods, String className) {
819 for (Iterator it = expressions.iterator(); it.hasNext();) {
820 ASTPrimaryExpression ape = (ASTPrimaryExpression) it.next();
821 MethodInvocation meth = findMethod(ape, className);
822 if (meth != null) {
823
824 calledMethods.add(meth);
825 }
826 }
827 }
828
829 /***
830 * @return A method call on the class passed in, or null if no method call
831 * is found.
832 * @todo Need a better way to match the class and package name to the actual
833 * method being called.
834 */
835 private static MethodInvocation findMethod(ASTPrimaryExpression node, String className) {
836 if (node.jjtGetNumChildren() > 0
837 && node.jjtGetChild(0).jjtGetNumChildren() > 0
838 && node.jjtGetChild(0).jjtGetChild(0) instanceof ASTLiteral) {
839 return null;
840 }
841 MethodInvocation meth = MethodInvocation.getMethod(node);
842 boolean found = false;
843
844
845
846 if (meth != null) {
847
848 if ((meth.getReferenceNames().size() == 0) && !meth.isSuper()) {
849
850
851 List packClass = meth.getQualifierNames();
852 if (packClass.size() > 0) {
853 for (Iterator it = packClass.iterator(); it.hasNext();) {
854 String name = (String) it.next();
855 if (name.equals(className)) {
856 found = true;
857 break;
858 }
859 }
860 } else {
861 found = true;
862 }
863 }
864 }
865
866 return found ? meth : null;
867 }
868
869 /***
870 * ASTPrimaryPrefix has name in child node of ASTName
871 */
872 private static String getNameFromPrefix(ASTPrimaryPrefix node) {
873 String name = null;
874
875 if (node.jjtGetNumChildren() == 1) {
876 Node nnode = node.jjtGetChild(0);
877 if (nnode instanceof ASTName) {
878 name = ((ASTName) nnode).getImage();
879 }
880 }
881 return name;
882 }
883
884 }