KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sourceforge > pmd > rules > ConstructorCallsOverridableMethod


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 JavaDoc;
25 import java.util.Collections JavaDoc;
26 import java.util.HashMap JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.List JavaDoc;
29 import java.util.Map JavaDoc;
30 import java.util.Set JavaDoc;
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 JavaDoc m_Name;
157         private ASTPrimaryExpression m_Ape;
158         private List JavaDoc m_ReferenceNames;
159         private List JavaDoc m_QualifierNames;
160         private int m_ArgumentSize;
161         private boolean m_Super;
162
163         private MethodInvocation(ASTPrimaryExpression ape, List JavaDoc qualifierNames, List JavaDoc referenceNames, String JavaDoc 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 JavaDoc getName() {
177             return m_Name;
178         }
179
180         public int getArgumentCount() {
181             return m_ArgumentSize;
182         }
183
184         public List JavaDoc getReferenceNames() {
185             return m_ReferenceNames;//new ArrayList(variableNames);
186
}
187
188         public List JavaDoc 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 JavaDoc varNames = new ArrayList JavaDoc();
206                     List JavaDoc packagesAndClasses = new ArrayList JavaDoc(); //look in JLS for better name here;
207
String JavaDoc 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 JavaDoc 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()) {
272                                         String JavaDoc 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 JavaDoc toParse = getNameFromPrefix(child);
286                                     // System.out.println("parsing for class/package names in : " + toParse);
287
java.util.StringTokenizer JavaDoc st = new java.util.StringTokenizer JavaDoc(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()) { //skip the () of method calls
299
String JavaDoc 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 JavaDoc toParse = getNameFromPrefix(child);
315                             // System.out.println("parsing for var names in : " + toParse);
316
java.util.StringTokenizer JavaDoc st = new java.util.StringTokenizer JavaDoc(toParse, ".");
317                             while (st.hasMoreTokens()) {
318                                 String JavaDoc 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()) {
334                                     String JavaDoc 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 JavaDoc pkg = getQualifierNames();
353             System.out.println(" <Qualifiers>");
354             for (Iterator JavaDoc it = pkg.iterator(); it.hasNext();) {
355                 String JavaDoc name = (String JavaDoc) it.next();
356                 System.out.println(" " + name);
357             }
358             System.out.println(" </Qualifiers>");
359             System.out.println(" <Super>" + isSuper() + "</Super>");
360             List JavaDoc vars = getReferenceNames();
361             System.out.println(" <References>");
362             for (Iterator JavaDoc it = vars.iterator(); it.hasNext();) {
363                 String JavaDoc name = (String JavaDoc) 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 JavaDoc name;
375         private int count = 0;
376
377         public ConstructorInvocation(ASTExplicitConstructorInvocation eci) {
378             m_Eci = eci;
379             List JavaDoc l = new ArrayList JavaDoc();
380             eci.findChildrenOfType(ASTArguments.class, l);
381             if (!l.isEmpty()) {
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 JavaDoc getName() {
397             return name;
398         }
399     }
400
401     private static final class MethodHolder {
402         private ASTMethodDeclarator amd;
403         private boolean dangerous;
404         private String JavaDoc called;
405
406         public MethodHolder(ASTMethodDeclarator amd) {
407             this.amd = amd;
408         }
409
410         public void setCalledMethod(String JavaDoc name) {
411             this.called = name;
412         }
413
414         public String JavaDoc getCalled() {
415             return this.called;
416         }
417
418         public ASTMethodDeclarator getASTMethodDeclarator() {
419             return amd;
420         }
421
422         public boolean isDangerous() {
423             return dangerous;
424         }
425
426         public void setDangerous() {
427             dangerous = true;
428         }
429     }
430
431     private final class ConstructorHolder {
432         private ASTConstructorDeclaration m_Cd;
433         private boolean m_Dangerous;
434         private ConstructorInvocation m_Ci;
435         private boolean m_CiInitialized;
436
437         public ConstructorHolder(ASTConstructorDeclaration cd) {
438             m_Cd = cd;
439         }
440
441         public ASTConstructorDeclaration getASTConstructorDeclaration() {
442             return m_Cd;
443         }
444
445         public ConstructorInvocation getCalledConstructor() {
446             if (!m_CiInitialized) {
447                 initCI();
448             }
449             return m_Ci;
450         }
451
452         public ASTExplicitConstructorInvocation getASTExplicitConstructorInvocation() {
453             ASTExplicitConstructorInvocation eci = null;
454             if (!m_CiInitialized) {
455                 initCI();
456             }
457             if (m_Ci != null) {
458                 eci = m_Ci.getASTExplicitConstructorInvocation();
459             }
460             return eci;
461         }
462
463         private void initCI() {
464             List JavaDoc expressions = new ArrayList JavaDoc();
465             m_Cd.findChildrenOfType(ASTExplicitConstructorInvocation.class, expressions); //only 1...
466
if (!expressions.isEmpty()) {
467                 ASTExplicitConstructorInvocation eci = (ASTExplicitConstructorInvocation) expressions.get(0);
468                 m_Ci = new ConstructorInvocation(eci);
469                 //System.out.println("Const call " + eci.getImage()); //super or this???
470
}
471             m_CiInitialized = true;
472         }
473
474         public boolean isDangerous() {
475             return m_Dangerous;
476         }
477
478         public void setDangerous(boolean dangerous) {
479             m_Dangerous = dangerous;
480         }
481     }
482
483     /**
484      * 1 package per class. holds info for evaluating a single class.
485      */

486     private static class EvalPackage {
487         public EvalPackage() {
488         }
489
490         public EvalPackage(String JavaDoc className) {
491             m_ClassName = className;
492             calledMethods = new ArrayList JavaDoc();//meths called from constructor
493
allMethodsOfClass = new HashMap JavaDoc();
494             calledConstructors = new ArrayList JavaDoc();//all constructors called from constructor
495
allPrivateConstructorsOfClass = new HashMap JavaDoc();
496         }
497
498         public String JavaDoc m_ClassName;
499         public List JavaDoc calledMethods;
500         public Map JavaDoc allMethodsOfClass;
501
502         public List JavaDoc calledConstructors;
503         public Map JavaDoc allPrivateConstructorsOfClass;
504     }
505
506     private static final class NullEvalPackage extends EvalPackage {
507         public NullEvalPackage() {
508             m_ClassName = "";
509             calledMethods = Collections.EMPTY_LIST;
510             allMethodsOfClass = Collections.EMPTY_MAP;
511             calledConstructors = Collections.EMPTY_LIST;
512             allPrivateConstructorsOfClass = Collections.EMPTY_MAP;
513         }
514     }
515
516     private static final NullEvalPackage nullEvalPackage = new NullEvalPackage();
517
518
519     /**
520      * 1 package per class.
521      */

522     private final List JavaDoc evalPackages = new ArrayList JavaDoc();//could use java.util.Stack
523

524     private EvalPackage getCurrentEvalPackage() {
525         return (EvalPackage) evalPackages.get(evalPackages.size() - 1);
526     }
527
528     /**
529      * Adds and evaluation package and makes it current
530      */

531     private void putEvalPackage(EvalPackage ep) {
532         evalPackages.add(ep);
533     }
534
535     private void removeCurrentEvalPackage() {
536         evalPackages.remove(evalPackages.size() - 1);
537     }
538
539     private void clearEvalPackages() {
540         evalPackages.clear();
541     }
542
543     /**
544      * This check must be evaluated independelty for each class. Inner classses
545      * get their own EvalPackage in order to perform independent evaluation.
546      */

547     private Object JavaDoc visitClassDec(ASTClassOrInterfaceDeclaration node, Object JavaDoc data) {
548         String JavaDoc className = node.getImage();
549         if (!node.isFinal() && !node.isStatic()) {
550             putEvalPackage(new EvalPackage(className));
551         } else {
552             putEvalPackage(nullEvalPackage);
553         }
554         //store any errors caught from other passes.
555
super.visit((ASTClassOrInterfaceDeclaration) node, data);
556
557         //skip this class if it has no evaluation package
558
if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {
559             //evaluate danger of all methods in class
560
while (evaluateDangerOfMethods(getCurrentEvalPackage().allMethodsOfClass)) {
561             }
562             //evaluate danger of constructors
563
evaluateDangerOfConstructors1(getCurrentEvalPackage().allPrivateConstructorsOfClass, getCurrentEvalPackage().allMethodsOfClass.keySet());
564             while (evaluateDangerOfConstructors2(getCurrentEvalPackage().allPrivateConstructorsOfClass)) {
565             }
566
567             //get each method called on this object from a non-private constructor, if its dangerous flag it
568
for (Iterator JavaDoc it = getCurrentEvalPackage().calledMethods.iterator(); it.hasNext();) {
569                 MethodInvocation meth = (MethodInvocation) it.next();
570                 //check against each dangerous method in class
571
for (Iterator JavaDoc it2 = getCurrentEvalPackage().allMethodsOfClass.keySet().iterator(); it2.hasNext();) {
572                     MethodHolder h = (MethodHolder) it2.next();
573                     if (h.isDangerous()) {
574                         String JavaDoc methName = h.getASTMethodDeclarator().getImage();
575                         int count = h.getASTMethodDeclarator().getParameterCount();
576                         if (methName.equals(meth.getName()) && meth.getArgumentCount() == count) {
577                             addViolation(data, meth.getASTPrimaryExpression(), "method '" + h.getCalled() + "'");
578                         }
579                     }
580                 }
581             }
582             //get each unsafe private constructor, and check if its called from any non private constructors
583
for (Iterator JavaDoc privConstIter = getCurrentEvalPackage().allPrivateConstructorsOfClass.keySet().iterator(); privConstIter.hasNext();) {
584                 ConstructorHolder ch = (ConstructorHolder) privConstIter.next();
585                 if (ch.isDangerous()) { //if its dangerous check if its called from any non-private constructors
586
//System.out.println("visitClassDec Evaluating dangerous constructor with " + ch.getASTConstructorDeclaration().getParameterCount() + " params");
587
int paramCount = ch.getASTConstructorDeclaration().getParameterCount();
588                     for (Iterator JavaDoc calledConstIter = getCurrentEvalPackage().calledConstructors.iterator(); calledConstIter.hasNext();) {
589                         ConstructorInvocation ci = (ConstructorInvocation) calledConstIter.next();
590                         if (ci.getArgumentCount() == paramCount) {
591                             //match name super / this !?
592
addViolation(data, ci.getASTExplicitConstructorInvocation(), "constructor");
593                         }
594                     }
595                 }
596             }
597         }
598         //finished evaluating this class, move up a level
599
removeCurrentEvalPackage();
600         return data;
601     }
602
603     /**
604      * Check the methods called on this class by each of the methods on this
605      * class. If a method calls an unsafe method, mark the calling method as
606      * unsafe. This changes the list of unsafe methods which necessitates
607      * another pass. Keep passing until you make a clean pass in which no
608      * methods are changed to unsafe.
609      * For speed it is possible to limit the number of passes.
610      * <p/>
611      * Impossible to tell type of arguments to method, so forget method matching
612      * on types. just use name and num of arguments. will be some false hits,
613      * but oh well.
614      *
615      * @todo investigate limiting the number of passes through config.
616      */

617     private boolean evaluateDangerOfMethods(Map JavaDoc classMethodMap) {
618         //check each method if it calls overridable method
619
boolean found = false;
620         for (Iterator JavaDoc methodsIter = classMethodMap.entrySet().iterator(); methodsIter.hasNext();) {
621             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) methodsIter.next();
622  
623             MethodHolder h = (MethodHolder) entry.getKey();
624             List JavaDoc calledMeths = (List JavaDoc) entry.getValue();
625             for (Iterator JavaDoc calledMethsIter = calledMeths.iterator(); calledMethsIter.hasNext() && !h.isDangerous();) {
626                 //if this method matches one of our dangerous methods, mark it dangerous
627
MethodInvocation meth = (MethodInvocation) calledMethsIter.next();
628                 //System.out.println("Called meth is " + meth);
629
for (Iterator JavaDoc innerMethsIter = classMethodMap.keySet().iterator(); innerMethsIter.hasNext();) { //need to skip self here h == h3
630
MethodHolder h3 = (MethodHolder) innerMethsIter.next();
631                     if (h3.isDangerous()) {
632                         String JavaDoc matchMethodName = h3.getASTMethodDeclarator().getImage();
633                         int matchMethodParamCount = h3.getASTMethodDeclarator().getParameterCount();
634                         //System.out.println("matching " + matchMethodName + " to " + meth.getName());
635
if (matchMethodName.equals(meth.getName()) && matchMethodParamCount == meth.getArgumentCount()) {
636                             h.setDangerous();
637                             h.setCalledMethod(matchMethodName);
638                             found = true;
639                             break;
640                         }
641                     }
642                 }
643             }
644         }
645         return found;
646     }
647
648     /**
649      * marks constructors dangerous if they call any dangerous methods
650      * Requires only a single pass as methods are already marked
651      *
652      * @todo optimize by having methods already evaluated somehow!?
653      */

654     private void evaluateDangerOfConstructors1(Map JavaDoc classConstructorMap, Set JavaDoc evaluatedMethods) {
655         //check each constructor in the class
656
for (Iterator JavaDoc constIter = classConstructorMap.entrySet().iterator(); constIter.hasNext();) {
657             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) constIter.next();
658             ConstructorHolder ch = (ConstructorHolder) entry.getKey();
659             if (!ch.isDangerous()) {//if its not dangerous then evaluate if it should be
660
//if it calls dangerous method mark it as dangerous
661
List JavaDoc calledMeths = (List JavaDoc) entry.getValue();
662                 //check each method it calls
663
for (Iterator JavaDoc calledMethsIter = calledMeths.iterator(); calledMethsIter.hasNext() && !ch.isDangerous();) {//but thee are diff objects which represent same thing but were never evaluated, they need reevaluation
664
MethodInvocation meth = (MethodInvocation) calledMethsIter.next();//CCE
665
String JavaDoc methName = meth.getName();
666                     int methArgCount = meth.getArgumentCount();
667                     //check each of the already evaluated methods: need to optimize this out
668
for (Iterator JavaDoc evaldMethsIter = evaluatedMethods.iterator(); evaldMethsIter.hasNext();) {
669                         MethodHolder h = (MethodHolder) evaldMethsIter.next();
670                         if (h.isDangerous()) {
671                             String JavaDoc matchName = h.getASTMethodDeclarator().getImage();
672                             int matchParamCount = h.getASTMethodDeclarator().getParameterCount();
673                             if (methName.equals(matchName) && (methArgCount == matchParamCount)) {
674                                 ch.setDangerous(true);
675                                 //System.out.println("evaluateDangerOfConstructors1 setting dangerous constructor with " + ch.getASTConstructorDeclaration().getParameterCount() + " params");
676
break;
677                             }
678                         }
679
680                     }
681                 }
682             }
683         }
684     }
685
686     /**
687      * Constructor map should contain a key for each private constructor, and
688      * maps to a List which contains all called constructors of that key.
689      * marks dangerous if call dangerous private constructor
690      * we ignore all non-private constructors here. That is, the map passed in
691      * should not contain any non-private constructors.
692      * we return boolean in order to limit the number of passes through this method
693      * but it seems as if we can forgo that and just process it till its done.
694      */

695     private boolean evaluateDangerOfConstructors2(Map JavaDoc classConstructorMap) {
696         boolean found = false;//triggers on danger state change
697
//check each constructor in the class
698
for (Iterator JavaDoc constIter = classConstructorMap.keySet().iterator(); constIter.hasNext();) {
699             ConstructorHolder ch = (ConstructorHolder) constIter.next();
700             ConstructorInvocation calledC = ch.getCalledConstructor();
701             if (calledC == null || ch.isDangerous()) {
702                 continue;
703             }
704             //if its not dangerous then evaluate if it should be
705
//if it calls dangerous constructor mark it as dangerous
706
int cCount = calledC.getArgumentCount();
707             for (Iterator JavaDoc innerConstIter = classConstructorMap.keySet().iterator(); innerConstIter.hasNext() && !ch.isDangerous();) { //forget skipping self because that introduces another check for each, but only 1 hit
708
ConstructorHolder h2 = (ConstructorHolder) innerConstIter.next();
709                 if (h2.isDangerous()) {
710                     int matchConstArgCount = h2.getASTConstructorDeclaration().getParameterCount();
711                     if (matchConstArgCount == cCount) {
712                         ch.setDangerous(true);
713                         found = true;
714                         //System.out.println("evaluateDangerOfConstructors2 setting dangerous constructor with " + ch.getASTConstructorDeclaration().getParameterCount() + " params");
715
}
716                 }
717             }
718         }
719         return found;
720     }
721
722     public Object JavaDoc visit(ASTCompilationUnit node, Object JavaDoc data) {
723         clearEvalPackages();
724         return super.visit(node, data);
725     }
726
727     public Object JavaDoc visit(ASTEnumDeclaration node, Object JavaDoc data) {
728         // just skip Enums
729
return data;
730     }
731
732     /**
733      * This check must be evaluated independelty for each class. Inner classses
734      * get their own EvalPackage in order to perform independent evaluation.
735      */

736     public Object JavaDoc visit(ASTClassOrInterfaceDeclaration node, Object JavaDoc data) {
737         if (!node.isInterface()) {
738             return visitClassDec(node, data);
739         } else {
740             putEvalPackage(nullEvalPackage);
741             Object JavaDoc o = super.visit(node, data);//interface may have inner classes, possible? if not just skip whole interface
742
removeCurrentEvalPackage();
743             return o;
744         }
745     }
746
747
748     /**
749      * Non-private constructor's methods are added to a list for later safety
750      * evaluation. Non-private constructor's calls on private constructors
751      * are added to a list for later safety evaluation. Private constructors
752      * are added to a list so their safety to be called can be later evaluated.
753      * <p/>
754      * Note: We are not checking private constructor's calls on non-private
755      * constructors because all non-private constructors will be evaluated for
756      * safety anyway. This means we wont flag a private constructor as unsafe
757      * just because it calls an unsafe public constructor. We want to show only
758      * 1 instance of an error, and this would be 2 instances of the same error.
759      *
760      * @todo eliminate the redundency
761      */

762     public Object JavaDoc visit(ASTConstructorDeclaration node, Object JavaDoc data) {
763         if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {//only evaluate if we have an eval package for this class
764
List JavaDoc calledMethodsOfConstructor = new ArrayList JavaDoc();
765             ConstructorHolder ch = new ConstructorHolder(node);
766             addCalledMethodsOfNode(node, calledMethodsOfConstructor, getCurrentEvalPackage().m_ClassName);
767             if (!node.isPrivate()) {
768                 //these calledMethods are what we will evaluate for being called badly
769
getCurrentEvalPackage().calledMethods.addAll(calledMethodsOfConstructor);
770                 //these called private constructors are what we will evaluate for being called badly
771
//we add all constructors invoked by non-private constructors
772
//but we are only interested in the private ones. We just can't tell the difference here
773
ASTExplicitConstructorInvocation eci = ch.getASTExplicitConstructorInvocation();
774                 if (eci != null && eci.isThis()) {
775                     getCurrentEvalPackage().calledConstructors.add(ch.getCalledConstructor());
776                 }
777             } else {
778                 //add all private constructors to list for later evaluation on if they are safe to call from another constructor
779
//store this constructorHolder for later evaluation
780
getCurrentEvalPackage().allPrivateConstructorsOfClass.put(ch, calledMethodsOfConstructor);
781             }
782         }
783         return super.visit(node, data);
784     }
785
786     /**
787      * Create a MethodHolder to hold the method.
788      * Store the MethodHolder in the Map as the key
789      * Store each method called by the current method as a List in the Map as the Object
790      */

791     public Object JavaDoc visit(ASTMethodDeclarator node, Object JavaDoc data) {
792         if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {//only evaluate if we have an eval package for this class
793
AccessNode parent = (AccessNode) node.jjtGetParent();
794             MethodHolder h = new MethodHolder(node);
795             if (!parent.isAbstract() && !parent.isPrivate() && !parent.isStatic() && !parent.isFinal()) { //Skip abstract methods, have a separate rule for that
796
h.setDangerous();//this method is overridable
797
ASTMethodDeclaration decl = (ASTMethodDeclaration) node.getFirstParentOfType(ASTMethodDeclaration.class);
798                 h.setCalledMethod(decl.getMethodName());
799             }
800             List JavaDoc l = new ArrayList JavaDoc();
801             addCalledMethodsOfNode((SimpleNode) parent, l, getCurrentEvalPackage().m_ClassName);
802             getCurrentEvalPackage().allMethodsOfClass.put(h, l);
803         }
804         return super.visit(node, data);
805     }
806
807
808     private static void addCalledMethodsOfNode(AccessNode node, List JavaDoc calledMethods, String JavaDoc className) {
809         List JavaDoc expressions = new ArrayList JavaDoc();
810         node.findChildrenOfType(ASTPrimaryExpression.class, expressions, false);
811         addCalledMethodsOfNodeImpl(expressions, calledMethods, className);
812     }
813
814     /**
815      * Adds all methods called on this instance from within this Node.
816      */

817     private static void addCalledMethodsOfNode(SimpleNode node, List JavaDoc calledMethods, String JavaDoc className) {
818         List JavaDoc expressions = new ArrayList JavaDoc();
819         node.findChildrenOfType(ASTPrimaryExpression.class, expressions);
820         addCalledMethodsOfNodeImpl(expressions, calledMethods, className);
821     }
822
823     private static void addCalledMethodsOfNodeImpl(List JavaDoc expressions, List JavaDoc calledMethods, String JavaDoc className) {
824         for (Iterator JavaDoc it = expressions.iterator(); it.hasNext();) {
825             ASTPrimaryExpression ape = (ASTPrimaryExpression) it.next();
826             MethodInvocation meth = findMethod(ape, className);
827             if (meth != null) {
828                 //System.out.println("Adding call " + methName);
829
calledMethods.add(meth);
830             }
831         }
832     }
833
834     /**
835      * @return A method call on the class passed in, or null if no method call
836      * is found.
837      * @todo Need a better way to match the class and package name to the actual
838      * method being called.
839      */

840     private static MethodInvocation findMethod(ASTPrimaryExpression node, String JavaDoc className) {
841         if (node.jjtGetNumChildren() > 0
842                 && node.jjtGetChild(0).jjtGetNumChildren() > 0
843                 && node.jjtGetChild(0).jjtGetChild(0) instanceof ASTLiteral) {
844             return null;
845         }
846         MethodInvocation meth = MethodInvocation.getMethod(node);
847         boolean found = false;
848         // if(meth != null){
849
// meth.show();
850
// }
851
if (meth != null) {
852             //if it's a call on a variable, or on its superclass ignore it.
853
if ((meth.getReferenceNames().size() == 0) && !meth.isSuper()) {
854                 //if this list does not contain our class name, then its not referencing our class
855
//this is a cheezy test... but it errs on the side of less false hits.
856
List JavaDoc packClass = meth.getQualifierNames();
857                 if (!packClass.isEmpty()) {
858                     for (Iterator JavaDoc it = packClass.iterator(); it.hasNext();) {
859                         String JavaDoc name = (String JavaDoc) it.next();
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 JavaDoc getNameFromPrefix(ASTPrimaryPrefix node) {
878         String JavaDoc 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 = ((ASTName) nnode).getImage();
884             }
885         }
886         return name;
887     }
888
889 }
890
Popular Tags