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.RuleContext;
8 import net.sourceforge.pmd.ast.ASTArguments;
9 import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
10 import net.sourceforge.pmd.ast.ASTCompilationUnit;
11 import net.sourceforge.pmd.ast.ASTConstructorDeclaration;
12 import net.sourceforge.pmd.ast.ASTExplicitConstructorInvocation;
13 import net.sourceforge.pmd.ast.ASTLiteral;
14 import net.sourceforge.pmd.ast.ASTMethodDeclarator;
15 import net.sourceforge.pmd.ast.ASTName;
16 import net.sourceforge.pmd.ast.ASTPrimaryExpression;
17 import net.sourceforge.pmd.ast.ASTPrimaryPrefix;
18 import net.sourceforge.pmd.ast.ASTPrimarySuffix;
19 import net.sourceforge.pmd.ast.AccessNode;
20 import net.sourceforge.pmd.ast.Node;
21 import net.sourceforge.pmd.ast.SimpleNode;
22
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30 import java.text.MessageFormat;
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 m_Amd;
403 private boolean m_Dangerous = false;
404
405 public MethodHolder(ASTMethodDeclarator amd) {
406 m_Amd = amd;
407 }
408
409 public ASTMethodDeclarator getASTMethodDeclarator() {
410 return m_Amd;
411 }
412
413 public boolean isDangerous() {
414 return m_Dangerous;
415 }
416
417 public void setDangerous(boolean dangerous) {
418 m_Dangerous = dangerous;
419 }
420 }
421
422 private final class ConstructorHolder {
423 private ASTConstructorDeclaration m_Cd;
424 private boolean m_Dangerous;
425 private ConstructorInvocation m_Ci;
426 private boolean m_CiInitialized;
427
428 public ConstructorHolder(ASTConstructorDeclaration cd) {
429 m_Cd = cd;
430 }
431
432 public ASTConstructorDeclaration getASTConstructorDeclaration() {
433 return m_Cd;
434 }
435
436 public ConstructorInvocation getCalledConstructor() {
437 if (m_CiInitialized == false) {
438 initCI();
439 }
440 return m_Ci;
441 }
442
443 public ASTExplicitConstructorInvocation getASTExplicitConstructorInvocation() {
444 ASTExplicitConstructorInvocation eci = null;
445 if (m_CiInitialized == false) {
446 initCI();
447 }
448 if (m_Ci != null) {
449 eci = m_Ci.getASTExplicitConstructorInvocation();
450 }
451 return eci;
452 }
453
454 private void initCI() {
455 List expressions = new ArrayList();
456 m_Cd.findChildrenOfType(ASTExplicitConstructorInvocation.class, expressions);
457 if (!expressions.isEmpty()) {
458 ASTExplicitConstructorInvocation eci = (ASTExplicitConstructorInvocation) expressions.get(0);
459 m_Ci = new ConstructorInvocation(eci);
460
461 }
462 m_CiInitialized = true;
463 }
464
465 public boolean isDangerous() {
466 return m_Dangerous;
467 }
468
469 public void setDangerous(boolean dangerous) {
470 m_Dangerous = dangerous;
471 }
472 }
473
474 /***
475 * 1 package per class. holds info for evaluating a single class.
476 */
477 private static class EvalPackage {
478 public EvalPackage() {
479 }
480
481 public EvalPackage(String className) {
482 m_ClassName = className;
483 calledMethods = new ArrayList();
484 allMethodsOfClass = new HashMap();
485 calledConstructors = new ArrayList();
486 allPrivateConstructorsOfClass = new HashMap();
487 }
488
489 public String m_ClassName;
490 public List calledMethods;
491 public Map allMethodsOfClass;
492
493 public List calledConstructors;
494 public Map allPrivateConstructorsOfClass;
495 }
496
497 private static final class NullEvalPackage extends EvalPackage {
498 public NullEvalPackage() {
499 m_ClassName = "";
500 calledMethods = Collections.EMPTY_LIST;
501 allMethodsOfClass = Collections.EMPTY_MAP;
502 calledConstructors = Collections.EMPTY_LIST;
503 allPrivateConstructorsOfClass = Collections.EMPTY_MAP;
504 }
505 }
506
507 private static final NullEvalPackage nullEvalPackage = new NullEvalPackage();
508
509
510 /***
511 * 1 package per class.
512 */
513 private final List evalPackages = new ArrayList();
514
515 private EvalPackage getCurrentEvalPackage() {
516 return (EvalPackage) evalPackages.get(evalPackages.size() - 1);
517 }
518
519 /***
520 * Adds and evaluation package and makes it current
521 */
522 private void putEvalPackage(EvalPackage ep) {
523 evalPackages.add(ep);
524 }
525
526 private void removeCurrentEvalPackage() {
527 evalPackages.remove(evalPackages.size() - 1);
528 }
529
530 private void clearEvalPackages() {
531 evalPackages.clear();
532 }
533
534 /***
535 * This check must be evaluated independelty for each class. Inner classses
536 * get their own EvalPackage in order to perform independent evaluation.
537 */
538 private Object visitClassDec(ASTClassOrInterfaceDeclaration node, Object data) {
539 String className = node.getImage();
540 if (!node.isFinal() && !node.isStatic()) {
541 putEvalPackage(new EvalPackage(className));
542 } else {
543 putEvalPackage(nullEvalPackage);
544 }
545
546 super.visit((ASTClassOrInterfaceDeclaration) node, data);
547
548
549 if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {
550
551 while (evaluateDangerOfMethods(getCurrentEvalPackage().allMethodsOfClass)) {}
552
553 evaluateDangerOfConstructors1(getCurrentEvalPackage().allPrivateConstructorsOfClass, getCurrentEvalPackage().allMethodsOfClass.keySet());
554 while (evaluateDangerOfConstructors2(getCurrentEvalPackage().allPrivateConstructorsOfClass)) {}
555
556
557 for (Iterator it = getCurrentEvalPackage().calledMethods.iterator(); it.hasNext();) {
558 MethodInvocation meth = (MethodInvocation) it.next();
559
560 for (Iterator it2 = getCurrentEvalPackage().allMethodsOfClass.keySet().iterator(); it2.hasNext();) {
561 MethodHolder h = (MethodHolder) it2.next();
562 if (h.isDangerous()) {
563 String methName = h.getASTMethodDeclarator().getImage();
564 int count = h.getASTMethodDeclarator().getParameterCount();
565 if (meth.getName().equals(methName) && (meth.getArgumentCount() == count)) {
566 RuleContext ctx = (RuleContext) data;
567 String msg = MessageFormat.format(getMessage(), new Object[]{meth.getName()});
568 ctx.getReport().addRuleViolation(createRuleViolation(ctx, meth.getASTPrimaryExpression(), msg));
569 }
570 }
571 }
572 }
573
574 for (Iterator privConstIter = getCurrentEvalPackage().allPrivateConstructorsOfClass.keySet().iterator(); privConstIter.hasNext();) {
575 ConstructorHolder ch = (ConstructorHolder) privConstIter.next();
576 if (ch.isDangerous()) {
577
578 int paramCount = ch.getASTConstructorDeclaration().getParameterCount();
579 for (Iterator calledConstIter = getCurrentEvalPackage().calledConstructors.iterator(); calledConstIter.hasNext();) {
580 ConstructorInvocation ci = (ConstructorInvocation) calledConstIter.next();
581 if (ci.getArgumentCount() == paramCount) {
582
583 RuleContext ctx = (RuleContext) data;
584 ctx.getReport().addRuleViolation(createRuleViolation(ctx, ci.getASTExplicitConstructorInvocation()));
585 }
586 }
587 }
588 }
589 }
590
591 removeCurrentEvalPackage();
592 return data;
593 }
594 /***
595 * Check the methods called on this class by each of the methods on this
596 * class. If a method calls an unsafe method, mark the calling method as
597 * unsafe. This changes the list of unsafe methods which necessitates
598 * another pass. Keep passing until you make a clean pass in which no
599 * methods are changed to unsafe.
600 * For speed it is possible to limit the number of passes.
601 * <p/>
602 * Impossible to tell type of arguments to method, so forget method matching
603 * on types. just use name and num of arguments. will be some false hits,
604 * but oh well.
605 *
606 * @todo investigate limiting the number of passes through config.
607 */
608 private boolean evaluateDangerOfMethods(Map classMethodMap) {
609
610 boolean found = false;
611 for (Iterator methodsIter = classMethodMap.keySet().iterator(); methodsIter.hasNext();) {
612 MethodHolder h = (MethodHolder) methodsIter.next();
613 List calledMeths = (List) classMethodMap.get(h);
614 for (Iterator calledMethsIter = calledMeths.iterator(); calledMethsIter.hasNext() && (h.isDangerous() == false);) {
615
616 MethodInvocation meth = (MethodInvocation) calledMethsIter.next();
617
618 for (Iterator innerMethsIter = classMethodMap.keySet().iterator(); innerMethsIter.hasNext();) {
619 MethodHolder h3 = (MethodHolder) innerMethsIter.next();
620 if (h3.isDangerous()) {
621 String matchMethodName = h3.getASTMethodDeclarator().getImage();
622 int matchMethodParamCount = h3.getASTMethodDeclarator().getParameterCount();
623
624 if (matchMethodName.equals(meth.getName()) && (matchMethodParamCount == meth.getArgumentCount())) {
625 h.setDangerous(true);
626 found = true;
627 break;
628 }
629 }
630 }
631 }
632 }
633 return found;
634 }
635
636 /***
637 * marks constructors dangerous if they call any dangerous methods
638 * Requires only a single pass as methods are already marked
639 *
640 * @todo optimize by having methods already evaluated somehow!?
641 */
642 private void evaluateDangerOfConstructors1(Map classConstructorMap, Set evaluatedMethods) {
643
644 for (Iterator constIter = classConstructorMap.keySet().iterator(); constIter.hasNext();) {
645 ConstructorHolder ch = (ConstructorHolder) constIter.next();
646 if (!ch.isDangerous()) {
647
648 List calledMeths = (List) classConstructorMap.get(ch);
649
650 for (Iterator calledMethsIter = calledMeths.iterator(); calledMethsIter.hasNext() && !ch.isDangerous();) {
651 MethodInvocation meth = (MethodInvocation) calledMethsIter.next();
652 String methName = meth.getName();
653 int methArgCount = meth.getArgumentCount();
654
655 for (Iterator evaldMethsIter = evaluatedMethods.iterator(); evaldMethsIter.hasNext();) {
656 MethodHolder h = (MethodHolder) evaldMethsIter.next();
657 if (h.isDangerous()) {
658 String matchName = h.getASTMethodDeclarator().getImage();
659 int matchParamCount = h.getASTMethodDeclarator().getParameterCount();
660 if (methName.equals(matchName) && (methArgCount == matchParamCount)) {
661 ch.setDangerous(true);
662
663 break;
664 }
665 }
666
667 }
668 }
669 }
670 }
671 }
672
673 /***
674 * Constructor map should contain a key for each private constructor, and
675 * maps to a List which contains all called constructors of that key.
676 * marks dangerous if call dangerous private constructor
677 * we ignore all non-private constructors here. That is, the map passed in
678 * should not contain any non-private constructors.
679 * we return boolean in order to limit the number of passes through this method
680 * but it seems as if we can forgo that and just process it till its done.
681 */
682 private boolean evaluateDangerOfConstructors2(Map classConstructorMap) {
683 boolean found = false;
684
685 for (Iterator constIter = classConstructorMap.keySet().iterator(); constIter.hasNext();) {
686 ConstructorHolder ch = (ConstructorHolder) constIter.next();
687 ConstructorInvocation calledC = ch.getCalledConstructor();
688 if (calledC == null || ch.isDangerous()) {
689 continue;
690 }
691
692
693 int cCount = calledC.getArgumentCount();
694 for (Iterator innerConstIter = classConstructorMap.keySet().iterator(); innerConstIter.hasNext() && !ch.isDangerous();) {
695 ConstructorHolder h2 = (ConstructorHolder) innerConstIter.next();
696 if (h2.isDangerous()) {
697 int matchConstArgCount = h2.getASTConstructorDeclaration().getParameterCount();
698 if (matchConstArgCount == cCount) {
699 ch.setDangerous(true);
700 found = true;
701
702 }
703 }
704 }
705 }
706 return found;
707 }
708
709 public Object visit(ASTCompilationUnit node, Object data) {
710 clearEvalPackages();
711 return super.visit(node, data);
712 }
713
714 /***
715 * This check must be evaluated independelty for each class. Inner classses
716 * get their own EvalPackage in order to perform independent evaluation.
717 */
718 public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
719 if (!node.isInterface()) {
720 return visitClassDec(node, data);
721 } else {
722 putEvalPackage(nullEvalPackage);
723 Object o = super.visit(node, data);
724 removeCurrentEvalPackage();
725 return o;
726 }
727 }
728
729
730 /***
731 * Non-private constructor's methods are added to a list for later safety
732 * evaluation. Non-private constructor's calls on private constructors
733 * are added to a list for later safety evaluation. Private constructors
734 * are added to a list so their safety to be called can be later evaluated.
735 * <p/>
736 * Note: We are not checking private constructor's calls on non-private
737 * constructors because all non-private constructors will be evaluated for
738 * safety anyway. This means we wont flag a private constructor as unsafe
739 * just because it calls an unsafe public constructor. We want to show only
740 * 1 instance of an error, and this would be 2 instances of the same error.
741 *
742 * @todo eliminate the redundency
743 */
744 public Object visit(ASTConstructorDeclaration node, Object data) {
745 if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {
746 List calledMethodsOfConstructor = new ArrayList();
747 ConstructorHolder ch = new ConstructorHolder(node);
748 addCalledMethodsOfNode(node, calledMethodsOfConstructor, getCurrentEvalPackage().m_ClassName);
749 if (!node.isPrivate()) {
750
751 getCurrentEvalPackage().calledMethods.addAll(calledMethodsOfConstructor);
752
753
754
755 ASTExplicitConstructorInvocation eci = ch.getASTExplicitConstructorInvocation();
756 if (eci != null && eci.isThis()) {
757 getCurrentEvalPackage().calledConstructors.add(ch.getCalledConstructor());
758 }
759 } else {
760
761
762 getCurrentEvalPackage().allPrivateConstructorsOfClass.put(ch, calledMethodsOfConstructor);
763 }
764 }
765 return super.visit(node, data);
766 }
767
768 /***
769 * Create a MethodHolder to hold the method.
770 * Store the MethodHolder in the Map as the key
771 * Store each method called by the current method as a List in the Map as the Object
772 */
773 public Object visit(ASTMethodDeclarator node, Object data) {
774 if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {
775 AccessNode parent = (AccessNode) node.jjtGetParent();
776 MethodHolder h = new MethodHolder(node);
777 if (!parent.isAbstract()) {
778 if (!parent.isPrivate() && !parent.isStatic() && !parent.isFinal()) {
779 h.setDangerous(true);
780 }
781 }
782 List l = new ArrayList();
783 addCalledMethodsOfNode((SimpleNode) parent, l, getCurrentEvalPackage().m_ClassName);
784 getCurrentEvalPackage().allMethodsOfClass.put(h, l);
785 }
786 return super.visit(node, data);
787 }
788
789
790
791 private static void addCalledMethodsOfNode(AccessNode node, List calledMethods, String className) {
792 List expressions = new ArrayList();
793 node.findChildrenOfType(ASTPrimaryExpression.class, expressions, false);
794 addCalledMethodsOfNodeImpl(expressions, calledMethods, className);
795 }
796
797 /***
798 * Adds all methods called on this instance from within this Node.
799 */
800 private static void addCalledMethodsOfNode(SimpleNode node, List calledMethods, String className) {
801 List expressions = new ArrayList();
802 node.findChildrenOfType(ASTPrimaryExpression.class, expressions);
803 addCalledMethodsOfNodeImpl(expressions, calledMethods, className);
804 }
805
806 private static void addCalledMethodsOfNodeImpl(List expressions, List calledMethods, String className) {
807 for (Iterator it = expressions.iterator(); it.hasNext();) {
808 ASTPrimaryExpression ape = (ASTPrimaryExpression) it.next();
809 MethodInvocation meth = findMethod(ape, className);
810 if (meth != null) {
811
812 calledMethods.add(meth);
813 }
814 }
815 }
816
817 /***
818 * @return A method call on the class passed in, or null if no method call
819 * is found.
820 * @todo Need a better way to match the class and package name to the actual
821 * method being called.
822 */
823 private static MethodInvocation findMethod(ASTPrimaryExpression node, String className) {
824 if (node.jjtGetNumChildren() > 0
825 && node.jjtGetChild(0).jjtGetNumChildren() > 0
826 && node.jjtGetChild(0).jjtGetChild(0) instanceof ASTLiteral) {
827 return null;
828 }
829 MethodInvocation meth = MethodInvocation.getMethod(node);
830 boolean found = false;
831
832
833
834 if (meth != null) {
835
836 if ((meth.getReferenceNames().size() == 0) && !meth.isSuper()) {
837
838
839 List packClass = meth.getQualifierNames();
840 if (packClass.size() > 0) {
841 for (Iterator it = packClass.iterator(); it.hasNext();) {
842 String name = (String) it.next();
843 if (name.equals(className)) {
844 found = true;
845 break;
846 }
847 }
848 } else {
849 found = true;
850 }
851 }
852 }
853
854 return found ? meth : null;
855 }
856
857 /***
858 * ASTPrimaryPrefix has name in child node of ASTName
859 */
860 private static String getNameFromPrefix(ASTPrimaryPrefix node) {
861 String name = null;
862
863 if (node.jjtGetNumChildren() == 1) {
864 Node nnode = node.jjtGetChild(0);
865 if (nnode instanceof ASTName) {
866 name = ((ASTName) nnode).getImage();
867 }
868 }
869 return name;
870 }
871
872 }