View Javadoc

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;//new ArrayList(variableNames);
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) {//should always be at least 2, probably can eliminate this check
200                 //start at end which is guaranteed, work backwards
201                 Node lastNode = node.jjtGetChild(i - 1);
202                 if ((lastNode.jjtGetNumChildren() == 1) && (lastNode.jjtGetChild(0) instanceof ASTArguments)) { //could be ASTExpression for instance 'a[4] = 5';
203                     //start putting method together
204                     //					System.out.println("Putting method together now");
205                     List varNames = new ArrayList();
206                     List packagesAndClasses = new ArrayList(); //look in JLS for better name here;
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                         //search all nodes except last for 'this' or 'super'.  will be at: (node 0 | node 1 | nowhere)
215                         //this is an ASTPrimarySuffix with a null image and does not have child (which will be of type ASTArguments)
216                         //this is an ASTPrimaryPrefix with a null image and an ASTName that has a null image
217                         //super is an ASTPrimarySuffix with a null image and does not have child (which will be of type ASTArguments)
218                         //super is an ASTPrimaryPrefix with a non-null image
219                         for (int x = 0; x < i - 1; x++) {
220                             Node child = node.jjtGetChild(x);
221                             if (child instanceof ASTPrimarySuffix) { //check suffix type match
222                                 ASTPrimarySuffix child2 = (ASTPrimarySuffix) child;
223                                 //								String name = getNameFromSuffix((ASTPrimarySuffix)child);
224                                 //								System.out.println("found name suffix of : " + name);
225                                 if (child2.getImage() == null && child2.jjtGetNumChildren() == 0) {
226                                     thisIndex = x;
227                                     break;
228                                 }
229                                 //could be super, could be this.  currently we cant tell difference so we miss super when
230                                 //XYZ.ClassName.super.method();
231                                 //still works though.
232                             } else if (child instanceof ASTPrimaryPrefix) { //check prefix type match
233                                 ASTPrimaryPrefix child2 = (ASTPrimaryPrefix) child;
234                                 if (getNameFromPrefix(child2) == null) {
235                                     if (child2.getImage() == null) {
236                                         thisIndex = x;
237                                         break;
238                                     } else {//happens when super is used [super.method(): image = 'method']
239                                         superFirst = true;
240                                         thisIndex = x;
241                                         //the true super is at an unusable index because super.method() has only 2 nodes [method=0,()=1]
242                                         //as opposed to the 3 you might expect and which this.method() actually has. [this=0,method=1.()=2]
243                                         break;
244                                     }
245                                 }
246                             }
247                             //							else{
248                             //								System.err.println("Bad Format error"); //throw exception, quit evaluating this compilation node
249                             //							}
250                         }
251                     }
252 
253                     if (thisIndex != -1) {
254                         //						System.out.println("Found this or super: " + thisIndex);
255                         //Hack that must be removed if and when the patters of super.method() begins to logically match the rest of the patterns !!!
256                         if (superFirst) { //this is when super is the first node of statement.  no qualifiers, all variables or method
257                             //							System.out.println("super first");
258                             FIRSTNODE:{
259                                 ASTPrimaryPrefix child = (ASTPrimaryPrefix) node.jjtGetChild(0);
260                                 String name = child.getImage();//special case
261                                 if (i == 2) { //last named node = method name
262                                     methodName = name;
263                                 } else { //not the last named node so its only var name
264                                     varNames.add(name);
265                                 }
266                             }
267                             OTHERNODES:{ //variables
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) {//last node
274                                             methodName = name;
275                                         } else {//not the last named node so its only var name
276                                             varNames.add(name);
277                                         }
278                                     }
279                                 }
280                             }
281                         } else {//not super call
282                             FIRSTNODE:{
283                                 if (thisIndex == 1) {//qualifiers in node 0
284                                     ASTPrimaryPrefix child = (ASTPrimaryPrefix) node.jjtGetChild(0);
285                                     String toParse = getNameFromPrefix(child);
286                                     //									System.out.println("parsing for class/package names in : " + toParse);
287                                     java.util.StringTokenizer st = new java.util.StringTokenizer(toParse, ".");
288                                     while (st.hasMoreTokens()) {
289                                         packagesAndClasses.add(st.nextToken());
290                                     }
291                                 }
292                             }
293                             OTHERNODES:{ //other methods called in this statement are grabbed here
294                                 //this is at 0, then no Qualifiers
295                                 //this is at 1, the node 0 contains qualifiers
296                                 for (int x = thisIndex + 1; x < i - 1; x++) {//everything after this is var name or method name
297                                     ASTPrimarySuffix child = (ASTPrimarySuffix) node.jjtGetChild(x);
298                                     if (child.isArguments() == false) { //skip the () of method calls
299                                         String name = child.getImage();
300                                         //										System.out.println("Found suffix: " + suffixName);
301                                         if (x == i - 2) {
302                                             methodName = name;
303                                         } else {
304                                             varNames.add(name);
305                                         }
306                                     }
307                                 }
308                             }
309                         }
310                     } else { //if no this or super found, everything is method name or variable
311                         //System.out.println("no this found:");
312                         FIRSTNODE:{ //variable names are in the prefix + the first method call [a.b.c.x()]
313                             ASTPrimaryPrefix child = (ASTPrimaryPrefix) node.jjtGetChild(0);
314                             String toParse = getNameFromPrefix(child);
315                             //							System.out.println("parsing for var names in : " + toParse);
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) {//if this expression is 2 nodes long, then the last part of prefix is method name
321                                         methodName = value;
322                                     } else {
323                                         varNames.add(value);
324                                     }
325                                 } else { //variable name
326                                     varNames.add(value);
327                                 }
328                             }
329                         }
330                         OTHERNODES:{ //other methods called in this statement are grabbed here
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); //only 1...
457             if (!expressions.isEmpty()) {
458                 ASTExplicitConstructorInvocation eci = (ASTExplicitConstructorInvocation) expressions.get(0);
459                 m_Ci = new ConstructorInvocation(eci);
460                 //System.out.println("Const call " + eci.getImage()); //super or this???
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();//meths called from constructor
484             allMethodsOfClass = new HashMap();
485             calledConstructors = new ArrayList();//all constructors called from constructor
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();//could use java.util.Stack
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         //store any errors caught from other passes.
546         super.visit((ASTClassOrInterfaceDeclaration) node, data);
547 
548         //skip this class if it has no evaluation package
549         if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {
550             //evaluate danger of all methods in class
551             while (evaluateDangerOfMethods(getCurrentEvalPackage().allMethodsOfClass)) {}
552             //evaluate danger of constructors
553             evaluateDangerOfConstructors1(getCurrentEvalPackage().allPrivateConstructorsOfClass, getCurrentEvalPackage().allMethodsOfClass.keySet());
554             while (evaluateDangerOfConstructors2(getCurrentEvalPackage().allPrivateConstructorsOfClass)) {}
555 
556             //get each method called on this object from a non-private constructor, if its dangerous flag it
557             for (Iterator it = getCurrentEvalPackage().calledMethods.iterator(); it.hasNext();) {
558                 MethodInvocation meth = (MethodInvocation) it.next();
559                 //check against each dangerous method in class
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             //get each unsafe private constructor, and check if its called from any non private constructors
574             for (Iterator privConstIter = getCurrentEvalPackage().allPrivateConstructorsOfClass.keySet().iterator(); privConstIter.hasNext();) {
575                 ConstructorHolder ch = (ConstructorHolder) privConstIter.next();
576                 if (ch.isDangerous()) { //if its dangerous check if its called from any non-private constructors
577                     //System.out.println("visitClassDec Evaluating dangerous constructor with " + ch.getASTConstructorDeclaration().getParameterCount() + " params");
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                             //match name  super / this !?
583                             RuleContext ctx = (RuleContext) data;
584                             ctx.getReport().addRuleViolation(createRuleViolation(ctx, ci.getASTExplicitConstructorInvocation()));
585                         }
586                     }
587                 }
588             }
589         }
590         //finished evaluating this class, move up a level
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         //check each method if it calls overridable method
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                 //if this method matches one of our dangerous methods, mark it dangerous
616                 MethodInvocation meth = (MethodInvocation) calledMethsIter.next();
617                 //System.out.println("Called meth is " + meth);
618                 for (Iterator innerMethsIter = classMethodMap.keySet().iterator(); innerMethsIter.hasNext();) { //need to skip self here h == h3
619                     MethodHolder h3 = (MethodHolder) innerMethsIter.next();
620                     if (h3.isDangerous()) {
621                         String matchMethodName = h3.getASTMethodDeclarator().getImage();
622                         int matchMethodParamCount = h3.getASTMethodDeclarator().getParameterCount();
623                         //System.out.println("matchint " + matchMethodName + " to " + methName);
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         //check each constructor in the class
644         for (Iterator constIter = classConstructorMap.keySet().iterator(); constIter.hasNext();) {
645             ConstructorHolder ch = (ConstructorHolder) constIter.next();
646             if (!ch.isDangerous()) {//if its not dangerous then evaluate if it should be
647                 //if it calls dangerous method mark it as dangerous
648                 List calledMeths = (List) classConstructorMap.get(ch);
649                 //check each method it calls
650                 for (Iterator calledMethsIter = calledMeths.iterator(); calledMethsIter.hasNext() && !ch.isDangerous();) {//but thee are diff objects which represent same thing but were never evaluated, they need reevaluation
651                     MethodInvocation meth = (MethodInvocation) calledMethsIter.next();//CCE
652                     String methName = meth.getName();
653                     int methArgCount = meth.getArgumentCount();
654                     //check each of the already evaluated methods: need to optimize this out
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                                 //System.out.println("evaluateDangerOfConstructors1 setting dangerous constructor with " + ch.getASTConstructorDeclaration().getParameterCount() + " params");
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;//triggers on danger state change
684         //check each constructor in the class
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             //if its not dangerous then evaluate if it should be
692             //if it calls dangerous constructor mark it as dangerous
693             int cCount = calledC.getArgumentCount();
694             for (Iterator innerConstIter = classConstructorMap.keySet().iterator(); innerConstIter.hasNext() && !ch.isDangerous();) { //forget skipping self because that introduces another check for each, but only 1 hit
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                         //System.out.println("evaluateDangerOfConstructors2 setting dangerous constructor with " + ch.getASTConstructorDeclaration().getParameterCount() + " params");
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);//interface may have inner classes, possible? if not just skip whole interface
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)) {//only evaluate if we have an eval package for this class
746             List calledMethodsOfConstructor = new ArrayList();
747             ConstructorHolder ch = new ConstructorHolder(node);
748             addCalledMethodsOfNode(node, calledMethodsOfConstructor, getCurrentEvalPackage().m_ClassName);
749             if (!node.isPrivate()) {
750                 //these calledMethods are what we will evaluate for being called badly
751                 getCurrentEvalPackage().calledMethods.addAll(calledMethodsOfConstructor);
752                 //these called private constructors are what we will evaluate for being called badly
753                 //we add all constructors invoked by non-private constructors
754                 //but we are only interested in the private ones.  We just can't tell the difference here
755                 ASTExplicitConstructorInvocation eci = ch.getASTExplicitConstructorInvocation();
756                 if (eci != null && eci.isThis()) {
757                     getCurrentEvalPackage().calledConstructors.add(ch.getCalledConstructor());
758                 }
759             } else {
760                 //add all private constructors to list for later evaluation on if they are safe to call from another constructor
761                 //store this constructorHolder for later evaluation
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)) {//only evaluate if we have an eval package for this class
775             AccessNode parent = (AccessNode) node.jjtGetParent();
776             MethodHolder h = new MethodHolder(node);
777             if (!parent.isAbstract()) { //Skip abstract methods, have a separate rule for that
778             	if (!parent.isPrivate() && !parent.isStatic() && !parent.isFinal()) {
779             		h.setDangerous(true);//this method is overridable
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                 //System.out.println("Adding call " + methName);
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         //		if(meth != null){
832         //			meth.show();
833         //		}
834         if (meth != null) {
835             //if it's a call on a variable, or on its superclass ignore it.
836             if ((meth.getReferenceNames().size() == 0) && !meth.isSuper()) {
837                 //if this list does not contain our class name, then its not referencing our class
838                 //this is a cheezy test... but it errs on the side of less false hits.
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         //should only be 1 child, if more I need more knowledge
863         if (node.jjtGetNumChildren() == 1) { //safety check
864             Node nnode = node.jjtGetChild(0);
865             if (nnode instanceof ASTName) { //just as easy as null check and it should be an ASTName anyway
866                 name = ((ASTName) nnode).getImage();
867             }
868         }
869         return name;
870     }
871 
872 }