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