ConstructorCallsOverridableMethodRule.java
001 /**
002  * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003  */
004 package net.sourceforge.pmd.lang.java.rule.design;
005 
006 import java.util.ArrayList;
007 import java.util.Collections;
008 import java.util.Comparator;
009 import java.util.Iterator;
010 import java.util.List;
011 import java.util.Map;
012 import java.util.Set;
013 import java.util.TreeMap;
014 
015 import net.sourceforge.pmd.lang.ast.Node;
016 import net.sourceforge.pmd.lang.java.ast.ASTArguments;
017 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
018 import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
019 import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
020 import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
021 import net.sourceforge.pmd.lang.java.ast.ASTExplicitConstructorInvocation;
022 import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
023 import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
024 import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator;
025 import net.sourceforge.pmd.lang.java.ast.ASTName;
026 import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
027 import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
028 import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix;
029 import net.sourceforge.pmd.lang.java.ast.AccessNode;
030 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
031 
032 /**
033  * Searches through all methods and constructors called from constructors.  It
034  * marks as dangerous any call to overridable methods from non-private
035  * constructors.  It marks as dangerous any calls to dangerous private constructors
036  * from non-private constructors.
037  *
038  @author CL Gilbert (dnoyeb@users.sourceforge.net)
039  * @todo match parameter types.  Aggressively strips off any package names.  Normal
040  * compares the names as is.
041  * @todo What about interface declarations which can have internal classes
042  */
043 public final class ConstructorCallsOverridableMethodRule extends AbstractJavaRule {
044     /**
045      * 2: method();
046      * ASTPrimaryPrefix
047      * ASTName      image = "method"
048      * ASTPrimarySuffix
049      * *ASTArguments
050      * 3: a.method();
051      * ASTPrimaryPrefix ->
052      * ASTName      image = "a.method" ???
053      * ASTPrimarySuffix -> ()
054      * ASTArguments
055      * 3: this.method();
056      * ASTPrimaryPrefix -> this image=null
057      * ASTPrimarySuffix -> method
058      * ASTPrimarySuffix -> ()
059      * ASTArguments
060      <p/>
061      * super.method();
062      * ASTPrimaryPrefix -> image = "method"
063      * ASTPrimarySuffix -> image = null
064      * ASTArguments ->
065      <p/>
066      * super.a.method();
067      * ASTPrimaryPrefix -> image = "a"
068      * ASTPrimarySuffix -> image = "method"
069      * ASTPrimarySuffix -> image = null
070      * ASTArguments ->
071      <p/>
072      <p/>
073      * 4: this.a.method();
074      * ASTPrimaryPrefix -> image = null
075      * ASTPrimarySuffix -> image = "a"
076      * ASTPrimarySuffix -> image = "method"
077      * ASTPrimarySuffix ->
078      * ASTArguments
079      <p/>
080      * 4: ClassName.this.method();
081      * ASTPrimaryPrefix
082      * ASTName  image = "ClassName"
083      * ASTPrimarySuffix -> this image=null
084      * ASTPrimarySuffix -> image = "method"
085      * ASTPrimarySuffix -> ()
086      * ASTArguments
087      * 5: ClassName.this.a.method();
088      * ASTPrimaryPrefix
089      * ASTName image = "ClassName"
090      * ASTPrimarySuffix -> this image=null
091      * ASTPrimarySuffix -> image="a"
092      * ASTPrimarySuffix -> image="method"
093      * ASTPrimarySuffix -> ()
094      * ASTArguments
095      * 5: Package.ClassName.this.method();
096      * ASTPrimaryPrefix
097      * ASTName image ="Package.ClassName"
098      * ASTPrimarySuffix -> this image=null
099      * 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 name;
157         private ASTPrimaryExpression ape;
158         private List<String> referenceNames;
159         private List<String> qualifierNames;
160         private int argumentSize;
161         private boolean superCall;
162 
163         private MethodInvocation(ASTPrimaryExpression ape, List<String> qualifierNames, List<String> referenceNames, String name, int argumentSize, boolean superCall) {
164             this.ape = ape;
165             this.qualifierNames = qualifierNames;
166             this.referenceNames = referenceNames;
167             this.name = name;
168             this.argumentSize = argumentSize;
169             this.superCall = superCall;
170         }
171 
172         public boolean isSuper() {
173             return superCall;
174         }
175 
176         public String getName() {
177             return name;
178         }
179 
180         public int getArgumentCount() {
181             return argumentSize;
182         }
183 
184         public List<String> getReferenceNames() {
185             return referenceNames;//new ArrayList(variableNames);
186         }
187 
188         public List<String> getQualifierNames() {
189             return qualifierNames;
190         }
191 
192         public ASTPrimaryExpression getASTPrimaryExpression() {
193             return 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() == && lastNode.jjtGetChild(0instanceof ASTArguments) { //could be ASTExpression for instance 'a[4] = 5';
203                     //start putting method together
204                     //          System.out.println("Putting method together now");
205                     List<String> varNames = new ArrayList<String>();
206                     List<String> packagesAndClasses = new ArrayList<String>()//look in JLS for better name here;
207                     String methodName = null;
208                     ASTArguments args = (ASTArgumentslastNode.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 = (ASTPrimarySuffixchild;
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 = (ASTPrimaryPrefixchild;
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 = (ASTPrimaryPrefixnode.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 = (ASTPrimarySuffixchild;
271                                     if (!ps.isArguments()) {
272                                         String name = ((ASTPrimarySuffixchild).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 = (ASTPrimaryPrefixnode.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 = (ASTPrimarySuffixnode.jjtGetChild(x);
298                                     if (!child.isArguments()) { //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 = (ASTPrimaryPrefixnode.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 = (ASTPrimarySuffixnode.jjtGetChild(x);
333                                 if (!child.isArguments()) {
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             System.out.println("  <Qualifiers>");
353             for (String name: getQualifierNames()) {
354                 System.out.println("    " + name);
355             }
356             System.out.println("  </Qualifiers>");
357             System.out.println("  <Super>" + isSuper() "</Super>");
358             System.out.println("  <References>");
359             for (String name: getReferenceNames()) {
360                 System.out.println("    " + name);
361             }
362             System.out.println("  </References>");
363             System.out.println("  <Name>" + getName() "</Name>");
364             System.out.println("</MethodInvocation>");
365         }
366     }
367 
368     private static final class ConstructorInvocation {
369         private ASTExplicitConstructorInvocation eci;
370         private String name;
371         private int count = 0;
372 
373         public ConstructorInvocation(ASTExplicitConstructorInvocation eci) {
374             this.eci = eci;
375             List<ASTArguments> l = eci.findChildrenOfType(ASTArguments.class);
376             if (!l.isEmpty()) {
377                 ASTArguments aa = l.get(0);
378                 count = aa.getArgumentCount();
379             }
380             name = eci.getImage();
381         }
382 
383         public ASTExplicitConstructorInvocation getASTExplicitConstructorInvocation() {
384             return eci;
385         }
386 
387         public int getArgumentCount() {
388             return count;
389         }
390 
391         public String getName() {
392             return name;
393         }
394     }
395 
396     private static final class MethodHolder {
397         private ASTMethodDeclarator amd;
398         private boolean dangerous;
399         private String called;
400 
401         public MethodHolder(ASTMethodDeclarator amd) {
402             this.amd = amd;
403         }
404 
405         public void setCalledMethod(String name) {
406             this.called = name;
407         }
408 
409         public String getCalled() {
410             return this.called;
411         }
412 
413         public ASTMethodDeclarator getASTMethodDeclarator() {
414             return amd;
415         }
416 
417         public boolean isDangerous() {
418             return dangerous;
419         }
420 
421         public void setDangerous() {
422             dangerous = true;
423         }
424     }
425 
426     private final class ConstructorHolder {
427         private ASTConstructorDeclaration cd;
428         private boolean dangerous;
429         private ConstructorInvocation ci;
430         private boolean ciInitialized;
431 
432         public ConstructorHolder(ASTConstructorDeclaration cd) {
433             this.cd = cd;
434         }
435 
436         public ASTConstructorDeclaration getASTConstructorDeclaration() {
437             return cd;
438         }
439 
440         public ConstructorInvocation getCalledConstructor() {
441             if (!ciInitialized) {
442                 initCI();
443             }
444             return ci;
445         }
446 
447         public ASTExplicitConstructorInvocation getASTExplicitConstructorInvocation() {
448             ASTExplicitConstructorInvocation eci = null;
449             if (!ciInitialized) {
450                 initCI();
451             }
452             if (ci != null) {
453                 eci = ci.getASTExplicitConstructorInvocation();
454             }
455             return eci;
456         }
457 
458         private void initCI() {
459             List<ASTExplicitConstructorInvocation> expressions = cd.findChildrenOfType(ASTExplicitConstructorInvocation.class)//only 1...
460             if (!expressions.isEmpty()) {
461                 ASTExplicitConstructorInvocation eci = expressions.get(0);
462                 ci = new ConstructorInvocation(eci);
463                 //System.out.println("Const call " + eci.getImage()); //super or this???
464             }
465             ciInitialized = true;
466         }
467 
468         public boolean isDangerous() {
469             return dangerous;
470         }
471 
472         public void setDangerous(boolean dangerous) {
473             this.dangerous = dangerous;
474         }
475     }
476 
477     private static int compareNodes(Node n1, Node n2) {
478         int l1 = n1.getBeginLine();
479         int l2 = n2.getBeginLine();
480         if (l1 == l2) {
481             return n1.getBeginColumn() - n2.getBeginColumn();
482         }
483         return l1 - l2;
484     }
485 
486     private static class MethodHolderComparator implements Comparator<MethodHolder> {
487         public int compare(MethodHolder o1, MethodHolder o2) {
488             return compareNodes(o1.getASTMethodDeclarator(), o2.getASTMethodDeclarator());
489         }
490     }
491 
492     private static class ConstructorHolderComparator implements Comparator<ConstructorHolder> {
493         public int compare(ConstructorHolder o1, ConstructorHolder o2) {
494             return compareNodes(o1.getASTConstructorDeclaration(), o2.getASTConstructorDeclaration());
495         }
496     }
497 
498     /**
499      * 1 package per class. holds info for evaluating a single class.
500      */
501     private static class EvalPackage {
502         public EvalPackage() {
503         }
504 
505         public EvalPackage(String className) {
506             this.className = className;
507             this.calledMethods = new ArrayList<MethodInvocation>();//meths called from constructor
508             this.allMethodsOfClass = new TreeMap<MethodHolder, List<MethodInvocation>>(new MethodHolderComparator());
509             this.calledConstructors = new ArrayList<ConstructorInvocation>();//all constructors called from constructor
510             this.allPrivateConstructorsOfClass = new TreeMap<ConstructorHolder, List<MethodInvocation>>(new ConstructorHolderComparator());
511         }
512 
513         public String className;
514         public List<MethodInvocation> calledMethods;
515         public Map<MethodHolder, List<MethodInvocation>> allMethodsOfClass;
516 
517         public List<ConstructorInvocation> calledConstructors;
518         public Map<ConstructorHolder, List<MethodInvocation>> allPrivateConstructorsOfClass;
519     }
520 
521     private static final class NullEvalPackage extends EvalPackage {
522         public NullEvalPackage() {
523             className = "";
524             calledMethods = Collections.emptyList();
525             allMethodsOfClass = Collections.emptyMap();
526             calledConstructors = Collections.emptyList();
527             allPrivateConstructorsOfClass = Collections.emptyMap();
528         }
529     }
530 
531     private static final NullEvalPackage NULL_EVAL_PACKAGE = new NullEvalPackage();
532 
533 
534     /**
535      * 1 package per class.
536      */
537     private final List<EvalPackage> evalPackages = new ArrayList<EvalPackage>();//could use java.util.Stack
538 
539     private EvalPackage getCurrentEvalPackage() {
540         return evalPackages.get(evalPackages.size() 1);
541     }
542 
543     /**
544      * Adds and evaluation package and makes it current
545      */
546     private void putEvalPackage(EvalPackage ep) {
547         evalPackages.add(ep);
548     }
549 
550     private void removeCurrentEvalPackage() {
551         evalPackages.remove(evalPackages.size() 1);
552     }
553 
554     private void clearEvalPackages() {
555         evalPackages.clear();
556     }
557 
558     /**
559      * This check must be evaluated independently for each class. Inner classes
560      * get their own EvalPackage in order to perform independent evaluation.
561      */
562     private Object visitClassDec(ASTClassOrInterfaceDeclaration node, Object data) {
563         String className = node.getImage();
564         if (!node.isFinal()) {
565             putEvalPackage(new EvalPackage(className));
566         else {
567             putEvalPackage(NULL_EVAL_PACKAGE);
568         }
569         //store any errors caught from other passes.
570         super.visit(node, data);
571 
572         //skip this class if it has no evaluation package
573         if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {
574             //evaluate danger of all methods in class, this method will return false when all methods have been evaluated
575             while (evaluateDangerOfMethods(getCurrentEvalPackage().allMethodsOfClass)) { } //NOPMD
576             //evaluate danger of constructors
577             evaluateDangerOfConstructors1(getCurrentEvalPackage().allPrivateConstructorsOfClass, getCurrentEvalPackage().allMethodsOfClass.keySet());
578             while (evaluateDangerOfConstructors2(getCurrentEvalPackage().allPrivateConstructorsOfClass)) { } //NOPMD
579 
580             //get each method called on this object from a non-private constructor, if its dangerous flag it
581             for (MethodInvocation meth: getCurrentEvalPackage().calledMethods) {
582                 //check against each dangerous method in class
583                 for (MethodHolder h: getCurrentEvalPackage().allMethodsOfClass.keySet()) {
584                     if (h.isDangerous()) {
585                         String methName = h.getASTMethodDeclarator().getImage();
586                         int count = h.getASTMethodDeclarator().getParameterCount();
587                         if (methName.equals(meth.getName()) && meth.getArgumentCount() == count) {
588                             addViolation(data, meth.getASTPrimaryExpression()"method '" + h.getCalled() "'");
589                         }
590                     }
591                 }
592             }
593             //get each unsafe private constructor, and check if its called from any non private constructors
594             for (ConstructorHolder ch: getCurrentEvalPackage().allPrivateConstructorsOfClass.keySet()) {
595                 if (ch.isDangerous()) { //if its dangerous check if its called from any non-private constructors
596                     //System.out.println("visitClassDec Evaluating dangerous constructor with " + ch.getASTConstructorDeclaration().getParameterCount() + " params");
597                     int paramCount = ch.getASTConstructorDeclaration().getParameterCount();
598                     for (ConstructorInvocation ci: getCurrentEvalPackage().calledConstructors) {
599                         if (ci.getArgumentCount() == paramCount) {
600                             //match name  super / this !?
601                             addViolation(data, ci.getASTExplicitConstructorInvocation()"constructor");
602                         }
603                     }
604                 }
605             }
606         }
607         //finished evaluating this class, move up a level
608         removeCurrentEvalPackage();
609         return data;
610     }
611 
612     /**
613      * Check the methods called on this class by each of the methods on this
614      * class.  If a method calls an unsafe method, mark the calling method as
615      * unsafe.  This changes the list of unsafe methods which necessitates
616      * another pass.  Keep passing until you make a clean pass in which no
617      * methods are changed to unsafe.
618      * For speed it is possible to limit the number of passes.
619      <p/>
620      * Impossible to tell type of arguments to method, so forget method matching
621      * on types.  just use name and num of arguments.  will be some false hits,
622      * but oh well.
623      *
624      * @todo investigate limiting the number of passes through config.
625      */
626     private boolean evaluateDangerOfMethods(Map<MethodHolder, List<MethodInvocation>> classMethodMap) {
627         //check each method if it calls overridable method
628         boolean found = false;
629         for (Map.Entry<MethodHolder, List<MethodInvocation>> entry: classMethodMap.entrySet()) {
630             MethodHolder h = entry.getKey();
631             List<MethodInvocation> calledMeths = entry.getValue();
632             for (Iterator<MethodInvocation> calledMethsIter = calledMeths.iterator(); calledMethsIter.hasNext() && !h.isDangerous();) {
633                 //if this method matches one of our dangerous methods, mark it dangerous
634                 MethodInvocation meth = calledMethsIter.next();
635                 //System.out.println("Called meth is " + meth);
636                 for (MethodHolder h3: classMethodMap.keySet()) { //need to skip self here h == h3
637                     if (h3.isDangerous()) {
638                         String matchMethodName = h3.getASTMethodDeclarator().getImage();
639                         int matchMethodParamCount = h3.getASTMethodDeclarator().getParameterCount();
640                         //System.out.println("matching " + matchMethodName + " to " + meth.getName());
641                         if (matchMethodName.equals(meth.getName()) && matchMethodParamCount == meth.getArgumentCount()) {
642                             h.setDangerous();
643                             h.setCalledMethod(matchMethodName);
644                             found = true;
645                             break;
646                         }
647                     }
648                 }
649             }
650         }
651         return found;
652     }
653 
654     /**
655      * marks constructors dangerous if they call any dangerous methods
656      * Requires only a single pass as methods are already marked
657      *
658      * @todo optimize by having methods already evaluated somehow!?
659      */
660     private void evaluateDangerOfConstructors1(Map<ConstructorHolder, List<MethodInvocation>> classConstructorMap, Set<MethodHolder> evaluatedMethods) {
661         //check each constructor in the class
662         for (Map.Entry<ConstructorHolder, List<MethodInvocation>> entry: classConstructorMap.entrySet()) {
663             ConstructorHolder ch = entry.getKey();
664             if (!ch.isDangerous()) {//if its not dangerous then evaluate if it should be
665                 //if it calls dangerous method mark it as dangerous
666                 List<MethodInvocation> calledMeths = entry.getValue();
667                 //check each method it calls
668                 for (Iterator<MethodInvocation> calledMethsIter = calledMeths.iterator(); calledMethsIter.hasNext() && !ch.isDangerous();) {//but thee are diff objects which represent same thing but were never evaluated, they need reevaluation
669                     MethodInvocation meth = calledMethsIter.next();//CCE
670                     String methName = meth.getName();
671                     int methArgCount = meth.getArgumentCount();
672                     //check each of the already evaluated methods: need to optimize this out
673                     for (MethodHolder h: evaluatedMethods) {
674                         if (h.isDangerous()) {
675                             String matchName = h.getASTMethodDeclarator().getImage();
676                             int matchParamCount = h.getASTMethodDeclarator().getParameterCount();
677                             if (methName.equals(matchName&& methArgCount == matchParamCount) {
678                                 ch.setDangerous(true);
679                                 //System.out.println("evaluateDangerOfConstructors1 setting dangerous constructor with " + ch.getASTConstructorDeclaration().getParameterCount() + " params");
680                                 break;
681                             }
682                         }
683 
684                     }
685                 }
686             }
687         }
688     }
689 
690     /**
691      * Constructor map should contain a key for each private constructor, and
692      * maps to a List which contains all called constructors of that key.
693      * marks dangerous if call dangerous private constructor
694      * we ignore all non-private constructors here.  That is, the map passed in
695      * should not contain any non-private constructors.
696      * we return boolean in order to limit the number of passes through this method
697      * but it seems as if we can forgo that and just process it till its done.
698      */
699     private boolean evaluateDangerOfConstructors2(Map<ConstructorHolder, List<MethodInvocation>> classConstructorMap) {
700         boolean found = false;//triggers on danger state change
701         //check each constructor in the class
702         for (ConstructorHolder ch: classConstructorMap.keySet()) {
703             ConstructorInvocation calledC = ch.getCalledConstructor();
704             if (calledC == null || ch.isDangerous()) {
705                 continue;
706             }
707             //if its not dangerous then evaluate if it should be
708             //if it calls dangerous constructor mark it as dangerous
709             int cCount = calledC.getArgumentCount();
710             for (Iterator<ConstructorHolder> innerConstIter = classConstructorMap.keySet().iterator(); innerConstIter.hasNext() && !ch.isDangerous();) { //forget skipping self because that introduces another check for each, but only 1 hit
711                 ConstructorHolder h2 = innerConstIter.next();
712                 if (h2.isDangerous()) {
713                     int matchConstArgCount = h2.getASTConstructorDeclaration().getParameterCount();
714                     if (matchConstArgCount == cCount) {
715                         ch.setDangerous(true);
716                         found = true;
717                         //System.out.println("evaluateDangerOfConstructors2 setting dangerous constructor with " + ch.getASTConstructorDeclaration().getParameterCount() + " params");
718                     }
719                 }
720             }
721         }
722         return found;
723     }
724 
725     @Override
726     public Object visit(ASTCompilationUnit node, Object data) {
727         clearEvalPackages();
728         return super.visit(node, data);
729     }
730 
731     @Override
732     public Object visit(ASTEnumDeclaration node, Object data) {
733         // just skip Enums
734         return data;
735     }
736 
737     /**
738      * This check must be evaluated independelty for each class.  Inner classses
739      * get their own EvalPackage in order to perform independent evaluation.
740      */
741     @Override
742     public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
743         if (!node.isInterface()) {
744             return visitClassDec(node, data);
745         else {
746             putEvalPackage(NULL_EVAL_PACKAGE);
747             Object o = super.visit(node, data);//interface may have inner classes, possible? if not just skip whole interface
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     @Override
769     public Object visit(ASTConstructorDeclaration node, Object data) {
770         if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {//only evaluate if we have an eval package for this class
771             List<MethodInvocation> calledMethodsOfConstructor = new ArrayList<MethodInvocation>();
772             ConstructorHolder ch = new ConstructorHolder(node);
773             addCalledMethodsOfNode(node, calledMethodsOfConstructor, getCurrentEvalPackage().className);
774             if (!node.isPrivate()) {
775                 //these calledMethods are what we will evaluate for being called badly
776                 getCurrentEvalPackage().calledMethods.addAll(calledMethodsOfConstructor);
777                 //these called private constructors are what we will evaluate for being called badly
778                 //we add all constructors invoked by non-private constructors
779                 //but we are only interested in the private ones.  We just can't tell the difference here
780                 ASTExplicitConstructorInvocation eci = ch.getASTExplicitConstructorInvocation();
781                 if (eci != null && eci.isThis()) {
782                     getCurrentEvalPackage().calledConstructors.add(ch.getCalledConstructor());
783                 }
784             else {
785                 //add all private constructors to list for later evaluation on if they are safe to call from another constructor
786                 //store this constructorHolder for later evaluation
787                 getCurrentEvalPackage().allPrivateConstructorsOfClass.put(ch, calledMethodsOfConstructor);
788             }
789         }
790         return super.visit(node, data);
791     }
792 
793     /**
794      * Create a MethodHolder to hold the method.
795      * Store the MethodHolder in the Map as the key
796      * Store each method called by the current method as a List in the Map as the Object
797      */
798     @Override
799     public Object visit(ASTMethodDeclarator node, Object data) {
800         if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {//only evaluate if we have an eval package for this class
801             AccessNode parent = (AccessNodenode.jjtGetParent();
802             MethodHolder h = new MethodHolder(node);
803             if (!parent.isAbstract() && !parent.isPrivate() && !parent.isStatic() && !parent.isFinal()) { //Skip abstract methods, have a separate rule for that
804                 h.setDangerous();//this method is overridable
805                 ASTMethodDeclaration decl = node.getFirstParentOfType(ASTMethodDeclaration.class);
806                 h.setCalledMethod(decl.getMethodName());
807             }
808             List<MethodInvocation> l = new ArrayList<MethodInvocation>();
809             addCalledMethodsOfNode((Node)parent, l, getCurrentEvalPackage().className);
810             getCurrentEvalPackage().allMethodsOfClass.put(h, l);
811         }
812         return super.visit(node, data);
813     }
814 
815 
816     /**
817      * Adds all methods called on this instance from within this Node.
818      */
819     private static void addCalledMethodsOfNode(Node node, List<MethodInvocation> calledMethods, String className) {
820         List<ASTPrimaryExpression> expressions = new ArrayList<ASTPrimaryExpression>();
821         node.findDescendantsOfType(ASTPrimaryExpression.class, expressions, !(node instanceof AccessNode));
822         addCalledMethodsOfNodeImpl(expressions, calledMethods, className);
823     }
824 
825     private static void addCalledMethodsOfNodeImpl(List<ASTPrimaryExpression> expressions, List<MethodInvocation> calledMethods, String className) {
826         for (ASTPrimaryExpression ape: expressions) {
827             MethodInvocation meth = findMethod(ape, className);
828             if (meth != null) {
829                 //System.out.println("Adding call " + methName);
830                 calledMethods.add(meth);
831             }
832         }
833     }
834 
835     /**
836      @return A method call on the class passed in, or null if no method call
837      *         is found.
838      * @todo Need a better way to match the class and package name to the actual
839      * method being called.
840      */
841     private static MethodInvocation findMethod(ASTPrimaryExpression node, String className) {
842         if (node.jjtGetNumChildren() 0
843                 && node.jjtGetChild(0).jjtGetNumChildren() 0
844                 && node.jjtGetChild(0).jjtGetChild(0instanceof ASTLiteral) {
845             return null;
846         }
847         MethodInvocation meth = MethodInvocation.getMethod(node);
848         boolean found = false;
849         //    if(meth != null){
850         //      meth.show();
851         //    }
852         if (meth != null) {
853             //if it's a call on a variable, or on its superclass ignore it.
854             if (meth.getReferenceNames().size() == && !meth.isSuper()) {
855                 //if this list does not contain our class name, then its not referencing our class
856                 //this is a cheezy test... but it errs on the side of less false hits.
857                 List<String> packClass = meth.getQualifierNames();
858                 if (!packClass.isEmpty()) {
859                     for (String name: packClass) {
860                         if (name.equals(className)) {
861                             found = true;
862                             break;
863                         }
864                     }
865                 else {
866                     found = true;
867                 }
868             }
869         }
870 
871         return found ? meth : null;
872     }
873 
874     /**
875      * ASTPrimaryPrefix has name in child node of ASTName
876      */
877     private static String getNameFromPrefix(ASTPrimaryPrefix node) {
878         String name = null;
879         //should only be 1 child, if more I need more knowledge
880         if (node.jjtGetNumChildren() == 1) { //safety check
881             Node nnode = node.jjtGetChild(0);
882             if (nnode instanceof ASTName) { //just as easy as null check and it should be an ASTName anyway
883                 name = ((ASTNamennode).getImage();
884             }
885         }
886         return name;
887     }
888 
889 }