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