Clover coverage report - PMD - 3.7
Coverage timestamp: Wed May 31 2006 09:25:59 EDT
file stats: LOC: 886   Methods: 47
NCLOC: 551   Classes: 7
 
 Source file Conditionals Statements Methods TOTAL
ConstructorCallsOverridableMethod.java 50% 68.1% 95.7% 65.1%
coverage coverage
 1    /**
 2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
 3    */
 4    package net.sourceforge.pmd.rules;
 5   
 6    import net.sourceforge.pmd.AbstractRule;
 7    import net.sourceforge.pmd.ast.ASTArguments;
 8    import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
 9    import net.sourceforge.pmd.ast.ASTCompilationUnit;
 10    import net.sourceforge.pmd.ast.ASTConstructorDeclaration;
 11    import net.sourceforge.pmd.ast.ASTEnumDeclaration;
 12    import net.sourceforge.pmd.ast.ASTExplicitConstructorInvocation;
 13    import net.sourceforge.pmd.ast.ASTLiteral;
 14    import net.sourceforge.pmd.ast.ASTMethodDeclaration;
 15    import net.sourceforge.pmd.ast.ASTMethodDeclarator;
 16    import net.sourceforge.pmd.ast.ASTName;
 17    import net.sourceforge.pmd.ast.ASTPrimaryExpression;
 18    import net.sourceforge.pmd.ast.ASTPrimaryPrefix;
 19    import net.sourceforge.pmd.ast.ASTPrimarySuffix;
 20    import net.sourceforge.pmd.ast.AccessNode;
 21    import net.sourceforge.pmd.ast.Node;
 22    import net.sourceforge.pmd.ast.SimpleNode;
 23   
 24    import java.util.ArrayList;
 25    import java.util.Collections;
 26    import java.util.HashMap;
 27    import java.util.Iterator;
 28    import java.util.List;
 29    import java.util.Map;
 30    import java.util.Set;
 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  9 private MethodInvocation(ASTPrimaryExpression ape, List qualifierNames, List referenceNames, String name, int argumentSize, boolean superCall) {
 164  9 m_Ape = ape;
 165  9 m_QualifierNames = qualifierNames;
 166  9 m_ReferenceNames = referenceNames;
 167  9 m_Name = name;
 168  9 m_ArgumentSize = argumentSize;
 169  9 m_Super = superCall;
 170    }
 171   
 172  9 public boolean isSuper() {
 173  9 return m_Super;
 174    }
 175   
 176  6 public String getName() {
 177  6 return m_Name;
 178    }
 179   
 180  4 public int getArgumentCount() {
 181  4 return m_ArgumentSize;
 182    }
 183   
 184  9 public List getReferenceNames() {
 185  9 return m_ReferenceNames;//new ArrayList(variableNames);
 186    }
 187   
 188  9 public List getQualifierNames() {
 189  9 return m_QualifierNames;
 190    }
 191   
 192  3 public ASTPrimaryExpression getASTPrimaryExpression() {
 193  3 return m_Ape;
 194    }
 195   
 196  11 public static MethodInvocation getMethod(ASTPrimaryExpression node) {
 197  11 MethodInvocation meth = null;
 198  11 int i = node.jjtGetNumChildren();
 199  11 if (i > 1) {//should always be at least 2, probably can eliminate this check
 200    //start at end which is guaranteed, work backwards
 201  9 Node lastNode = node.jjtGetChild(i - 1);
 202  9 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  9 List varNames = new ArrayList();
 206  9 List packagesAndClasses = new ArrayList(); //look in JLS for better name here;
 207  9 String methodName = null;
 208  9 ASTArguments args = (ASTArguments) lastNode.jjtGetChild(0);
 209  9 int numOfArguments = args.getArgumentCount();
 210  9 boolean superFirst = false;
 211  9 int thisIndex = -1;
 212   
 213  9 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  9 for (int x = 0; x < i - 1; x++) {
 220  9 Node child = node.jjtGetChild(x);
 221  9 if (child instanceof ASTPrimarySuffix) { //check suffix type match
 222  0 ASTPrimarySuffix child2 = (ASTPrimarySuffix) child;
 223    // String name = getNameFromSuffix((ASTPrimarySuffix)child);
 224    // System.out.println("found name suffix of : " + name);
 225  0 if (child2.getImage() == null && child2.jjtGetNumChildren() == 0) {
 226  0 thisIndex = x;
 227  0 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  9 } else if (child instanceof ASTPrimaryPrefix) { //check prefix type match
 233  9 ASTPrimaryPrefix child2 = (ASTPrimaryPrefix) child;
 234  9 if (getNameFromPrefix(child2) == null) {
 235  0 if (child2.getImage() == null) {
 236  0 thisIndex = x;
 237  0 break;
 238    } else {//happens when super is used [super.method(): image = 'method']
 239  0 superFirst = true;
 240  0 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  0 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  9 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  0 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  0 FIRSTNODE:{
 259  0 ASTPrimaryPrefix child = (ASTPrimaryPrefix) node.jjtGetChild(0);
 260  0 String name = child.getImage();//special case
 261  0 if (i == 2) { //last named node = method name
 262  0 methodName = name;
 263    } else { //not the last named node so its only var name
 264  0 varNames.add(name);
 265    }
 266    }
 267  0 OTHERNODES:{ //variables
 268  0 for (int x = 1; x < i - 1; x++) {
 269  0 Node child = node.jjtGetChild(x);
 270  0 ASTPrimarySuffix ps = (ASTPrimarySuffix) child;
 271  0 if (ps.isArguments() == false) {
 272  0 String name = ((ASTPrimarySuffix) child).getImage();
 273  0 if (x == i - 2) {//last node
 274  0 methodName = name;
 275    } else {//not the last named node so its only var name
 276  0 varNames.add(name);
 277    }
 278    }
 279    }
 280    }
 281    } else {//not super call
 282  0 FIRSTNODE:{
 283  0 if (thisIndex == 1) {//qualifiers in node 0
 284  0 ASTPrimaryPrefix child = (ASTPrimaryPrefix) node.jjtGetChild(0);
 285  0 String toParse = getNameFromPrefix(child);
 286    // System.out.println("parsing for class/package names in : " + toParse);
 287  0 java.util.StringTokenizer st = new java.util.StringTokenizer(toParse, ".");
 288  0 while (st.hasMoreTokens()) {
 289  0 packagesAndClasses.add(st.nextToken());
 290    }
 291    }
 292    }
 293  0 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  0 for (int x = thisIndex + 1; x < i - 1; x++) {//everything after this is var name or method name
 297  0 ASTPrimarySuffix child = (ASTPrimarySuffix) node.jjtGetChild(x);
 298  0 if (child.isArguments() == false) { //skip the () of method calls
 299  0 String name = child.getImage();
 300    // System.out.println("Found suffix: " + suffixName);
 301  0 if (x == i - 2) {
 302  0 methodName = name;
 303    } else {
 304  0 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  9 FIRSTNODE:{ //variable names are in the prefix + the first method call [a.b.c.x()]
 313  9 ASTPrimaryPrefix child = (ASTPrimaryPrefix) node.jjtGetChild(0);
 314  9 String toParse = getNameFromPrefix(child);
 315    // System.out.println("parsing for var names in : " + toParse);
 316  9 java.util.StringTokenizer st = new java.util.StringTokenizer(toParse, ".");
 317  9 while (st.hasMoreTokens()) {
 318  9 String value = st.nextToken();
 319  9 if (!st.hasMoreTokens()) {
 320  9 if (i == 2) {//if this expression is 2 nodes long, then the last part of prefix is method name
 321  9 methodName = value;
 322    } else {
 323  0 varNames.add(value);
 324    }
 325    } else { //variable name
 326  0 varNames.add(value);
 327    }
 328    }
 329    }
 330  9 OTHERNODES:{ //other methods called in this statement are grabbed here
 331  9 for (int x = 1; x < i - 1; x++) {
 332  0 ASTPrimarySuffix child = (ASTPrimarySuffix) node.jjtGetChild(x);
 333  0 if (child.isArguments() == false) {
 334  0 String name = child.getImage();
 335  0 if (x == i - 2) {
 336  0 methodName = name;
 337    } else {
 338  0 varNames.add(name);
 339    }
 340    }
 341    }
 342    }
 343    }
 344  9 meth = new MethodInvocation(node, packagesAndClasses, varNames, methodName, numOfArguments, superFirst);
 345    }
 346    }
 347  11 return meth;
 348    }
 349   
 350  0 public void show() {
 351  0 System.out.println("<MethodInvocation>");
 352  0 List pkg = getQualifierNames();
 353  0 System.out.println(" <Qualifiers>");
 354  0 for (Iterator it = pkg.iterator(); it.hasNext();) {
 355  0 String name = (String) it.next();
 356  0 System.out.println(" " + name);
 357    }
 358  0 System.out.println(" </Qualifiers>");
 359  0 System.out.println(" <Super>" + isSuper() + "</Super>");
 360  0 List vars = getReferenceNames();
 361  0 System.out.println(" <References>");
 362  0 for (Iterator it = vars.iterator(); it.hasNext();) {
 363  0 String name = (String) it.next();
 364  0 System.out.println(" " + name);
 365    }
 366  0 System.out.println(" </References>");
 367  0 System.out.println(" <Name>" + getName() + "</Name>");
 368  0 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  1 public ConstructorInvocation(ASTExplicitConstructorInvocation eci) {
 378  1 m_Eci = eci;
 379  1 List l = new ArrayList();
 380  1 eci.findChildrenOfType(ASTArguments.class, l);
 381  1 if (l.size() > 0) {
 382  1 ASTArguments aa = (ASTArguments) l.get(0);
 383  1 count = aa.getArgumentCount();
 384    }
 385  1 name = eci.getImage();
 386    }
 387   
 388  2 public ASTExplicitConstructorInvocation getASTExplicitConstructorInvocation() {
 389  2 return m_Eci;
 390    }
 391   
 392  1 public int getArgumentCount() {
 393  1 return count;
 394    }
 395   
 396  0 public String getName() {
 397  0 return name;
 398    }
 399    }
 400   
 401    private static final class MethodHolder {
 402    private ASTMethodDeclarator amd;
 403    private boolean dangerous;
 404    private String called;
 405   
 406  10 public MethodHolder(ASTMethodDeclarator amd) {
 407  10 this.amd = amd;
 408    }
 409   
 410  7 public void setCalledMethod(String name) {
 411  7 this.called = name;
 412    }
 413   
 414  3 public String getCalled() {
 415  3 return this.called;
 416    }
 417   
 418  12 public ASTMethodDeclarator getASTMethodDeclarator() {
 419  12 return amd;
 420    }
 421   
 422  10 public boolean isDangerous() {
 423  10 return dangerous;
 424    }
 425   
 426  7 public void setDangerous() {
 427  7 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  10 public ConstructorHolder(ASTConstructorDeclaration cd) {
 438  10 m_Cd = cd;
 439    }
 440   
 441  1 public ASTConstructorDeclaration getASTConstructorDeclaration() {
 442  1 return m_Cd;
 443    }
 444   
 445  2 public ConstructorInvocation getCalledConstructor() {
 446  2 if (m_CiInitialized == false) {
 447  1 initCI();
 448    }
 449  2 return m_Ci;
 450    }
 451   
 452  9 public ASTExplicitConstructorInvocation getASTExplicitConstructorInvocation() {
 453  9 ASTExplicitConstructorInvocation eci = null;
 454  9 if (m_CiInitialized == false) {
 455  9 initCI();
 456    }
 457  9 if (m_Ci != null) {
 458  1 eci = m_Ci.getASTExplicitConstructorInvocation();
 459    }
 460  9 return eci;
 461    }
 462   
 463  10 private void initCI() {
 464  10 List expressions = new ArrayList();
 465  10 m_Cd.findChildrenOfType(ASTExplicitConstructorInvocation.class, expressions); //only 1...
 466  10 if (!expressions.isEmpty()) {
 467  1 ASTExplicitConstructorInvocation eci = (ASTExplicitConstructorInvocation) expressions.get(0);
 468  1 m_Ci = new ConstructorInvocation(eci);
 469    //System.out.println("Const call " + eci.getImage()); //super or this???
 470    }
 471  10 m_CiInitialized = true;
 472    }
 473   
 474  3 public boolean isDangerous() {
 475  3 return m_Dangerous;
 476    }
 477   
 478  1 public void setDangerous(boolean dangerous) {
 479  1 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  38 public EvalPackage() {
 488    }
 489   
 490  9 public EvalPackage(String className) {
 491  9 m_ClassName = className;
 492  9 calledMethods = new ArrayList();//meths called from constructor
 493  9 allMethodsOfClass = new HashMap();
 494  9 calledConstructors = new ArrayList();//all constructors called from constructor
 495  9 allPrivateConstructorsOfClass = new HashMap();
 496    }
 497   
 498    public String m_ClassName;
 499    public List calledMethods;
 500    public Map allMethodsOfClass;
 501   
 502    public List calledConstructors;
 503    public Map allPrivateConstructorsOfClass;
 504    }
 505   
 506    private static final class NullEvalPackage extends EvalPackage {
 507  38 public NullEvalPackage() {
 508  38 m_ClassName = "";
 509  38 calledMethods = Collections.EMPTY_LIST;
 510  38 allMethodsOfClass = Collections.EMPTY_MAP;
 511  38 calledConstructors = Collections.EMPTY_LIST;
 512  38 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 evalPackages = new ArrayList();//could use java.util.Stack
 523   
 524  132 private EvalPackage getCurrentEvalPackage() {
 525  132 return (EvalPackage) evalPackages.get(evalPackages.size() - 1);
 526    }
 527   
 528    /**
 529    * Adds and evaluation package and makes it current
 530    */
 531  9 private void putEvalPackage(EvalPackage ep) {
 532  9 evalPackages.add(ep);
 533    }
 534   
 535  9 private void removeCurrentEvalPackage() {
 536  9 evalPackages.remove(evalPackages.size() - 1);
 537    }
 538   
 539  10 private void clearEvalPackages() {
 540  10 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  9 private Object visitClassDec(ASTClassOrInterfaceDeclaration node, Object data) {
 548  9 String className = node.getImage();
 549  9 if (!node.isFinal() && !node.isStatic()) {
 550  9 putEvalPackage(new EvalPackage(className));
 551    } else {
 552  0 putEvalPackage(nullEvalPackage);
 553    }
 554    //store any errors caught from other passes.
 555  9 super.visit((ASTClassOrInterfaceDeclaration) node, data);
 556   
 557    //skip this class if it has no evaluation package
 558  9 if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {
 559    //evaluate danger of all methods in class
 560  9 while (evaluateDangerOfMethods(getCurrentEvalPackage().allMethodsOfClass)) {
 561    }
 562    //evaluate danger of constructors
 563  9 evaluateDangerOfConstructors1(getCurrentEvalPackage().allPrivateConstructorsOfClass, getCurrentEvalPackage().allMethodsOfClass.keySet());
 564  9 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  9 for (Iterator it = getCurrentEvalPackage().calledMethods.iterator(); it.hasNext();) {
 569  7 MethodInvocation meth = (MethodInvocation) it.next();
 570    //check against each dangerous method in class
 571  7 for (Iterator it2 = getCurrentEvalPackage().allMethodsOfClass.keySet().iterator(); it2.hasNext();) {
 572  8 MethodHolder h = (MethodHolder) it2.next();
 573  8 if (h.isDangerous()) {
 574  5 String methName = h.getASTMethodDeclarator().getImage();
 575  5 int count = h.getASTMethodDeclarator().getParameterCount();
 576  5 if (meth.getName().equals(methName) && meth.getArgumentCount() == count) {
 577  3 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  9 for (Iterator privConstIter = getCurrentEvalPackage().allPrivateConstructorsOfClass.keySet().iterator(); privConstIter.hasNext();) {
 584  1 ConstructorHolder ch = (ConstructorHolder) privConstIter.next();
 585  1 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  1 int paramCount = ch.getASTConstructorDeclaration().getParameterCount();
 588  1 for (Iterator calledConstIter = getCurrentEvalPackage().calledConstructors.iterator(); calledConstIter.hasNext();) {
 589  1 ConstructorInvocation ci = (ConstructorInvocation) calledConstIter.next();
 590  1 if (ci.getArgumentCount() == paramCount) {
 591    //match name super / this !?
 592  1 addViolation(data, ci.getASTExplicitConstructorInvocation(), "constructor");
 593    }
 594    }
 595    }
 596    }
 597    }
 598    //finished evaluating this class, move up a level
 599  9 removeCurrentEvalPackage();
 600  9 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  9 private boolean evaluateDangerOfMethods(Map classMethodMap) {
 618    //check each method if it calls overridable method
 619  9 boolean found = false;
 620  9 for (Iterator methodsIter = classMethodMap.keySet().iterator(); methodsIter.hasNext();) {
 621  10 MethodHolder h = (MethodHolder) methodsIter.next();
 622  10 List calledMeths = (List) classMethodMap.get(h);
 623  10 for (Iterator calledMethsIter = calledMeths.iterator(); calledMethsIter.hasNext() && (h.isDangerous() == false);) {
 624    //if this method matches one of our dangerous methods, mark it dangerous
 625  0 MethodInvocation meth = (MethodInvocation) calledMethsIter.next();
 626    //System.out.println("Called meth is " + meth);
 627  0 for (Iterator innerMethsIter = classMethodMap.keySet().iterator(); innerMethsIter.hasNext();) { //need to skip self here h == h3
 628  0 MethodHolder h3 = (MethodHolder) innerMethsIter.next();
 629  0 if (h3.isDangerous()) {
 630  0 String matchMethodName = h3.getASTMethodDeclarator().getImage();
 631  0 int matchMethodParamCount = h3.getASTMethodDeclarator().getParameterCount();
 632    //System.out.println("matching " + matchMethodName + " to " + meth.getName());
 633  0 if (matchMethodName.equals(meth.getName()) && matchMethodParamCount == meth.getArgumentCount()) {
 634  0 h.setDangerous();
 635  0 h.setCalledMethod(matchMethodName);
 636  0 found = true;
 637  0 break;
 638    }
 639    }
 640    }
 641    }
 642    }
 643  9 return found;
 644    }
 645   
 646    /**
 647    * marks constructors dangerous if they call any dangerous methods
 648    * Requires only a single pass as methods are already marked
 649    *
 650    * @todo optimize by having methods already evaluated somehow!?
 651    */
 652  9 private void evaluateDangerOfConstructors1(Map classConstructorMap, Set evaluatedMethods) {
 653    //check each constructor in the class
 654  9 for (Iterator constIter = classConstructorMap.keySet().iterator(); constIter.hasNext();) {
 655  1 ConstructorHolder ch = (ConstructorHolder) constIter.next();
 656  1 if (!ch.isDangerous()) {//if its not dangerous then evaluate if it should be
 657    //if it calls dangerous method mark it as dangerous
 658  1 List calledMeths = (List) classConstructorMap.get(ch);
 659    //check each method it calls
 660  1 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
 661  1 MethodInvocation meth = (MethodInvocation) calledMethsIter.next();//CCE
 662  1 String methName = meth.getName();
 663  1 int methArgCount = meth.getArgumentCount();
 664    //check each of the already evaluated methods: need to optimize this out
 665  1 for (Iterator evaldMethsIter = evaluatedMethods.iterator(); evaldMethsIter.hasNext();) {
 666  1 MethodHolder h = (MethodHolder) evaldMethsIter.next();
 667  1 if (h.isDangerous()) {
 668  1 String matchName = h.getASTMethodDeclarator().getImage();
 669  1 int matchParamCount = h.getASTMethodDeclarator().getParameterCount();
 670  1 if (methName.equals(matchName) && (methArgCount == matchParamCount)) {
 671  1 ch.setDangerous(true);
 672    //System.out.println("evaluateDangerOfConstructors1 setting dangerous constructor with " + ch.getASTConstructorDeclaration().getParameterCount() + " params");
 673  1 break;
 674    }
 675    }
 676   
 677    }
 678    }
 679    }
 680    }
 681    }
 682   
 683    /**
 684    * Constructor map should contain a key for each private constructor, and
 685    * maps to a List which contains all called constructors of that key.
 686    * marks dangerous if call dangerous private constructor
 687    * we ignore all non-private constructors here. That is, the map passed in
 688    * should not contain any non-private constructors.
 689    * we return boolean in order to limit the number of passes through this method
 690    * but it seems as if we can forgo that and just process it till its done.
 691    */
 692  9 private boolean evaluateDangerOfConstructors2(Map classConstructorMap) {
 693  9 boolean found = false;//triggers on danger state change
 694    //check each constructor in the class
 695  9 for (Iterator constIter = classConstructorMap.keySet().iterator(); constIter.hasNext();) {
 696  1 ConstructorHolder ch = (ConstructorHolder) constIter.next();
 697  1 ConstructorInvocation calledC = ch.getCalledConstructor();
 698  1 if (calledC == null || ch.isDangerous()) {
 699  1 continue;
 700    }
 701    //if its not dangerous then evaluate if it should be
 702    //if it calls dangerous constructor mark it as dangerous
 703  0 int cCount = calledC.getArgumentCount();
 704  0 for (Iterator innerConstIter = classConstructorMap.keySet().iterator(); innerConstIter.hasNext() && !ch.isDangerous();) { //forget skipping self because that introduces another check for each, but only 1 hit
 705  0 ConstructorHolder h2 = (ConstructorHolder) innerConstIter.next();
 706  0 if (h2.isDangerous()) {
 707  0 int matchConstArgCount = h2.getASTConstructorDeclaration().getParameterCount();
 708  0 if (matchConstArgCount == cCount) {
 709  0 ch.setDangerous(true);
 710  0 found = true;
 711    //System.out.println("evaluateDangerOfConstructors2 setting dangerous constructor with " + ch.getASTConstructorDeclaration().getParameterCount() + " params");
 712    }
 713    }
 714    }
 715    }
 716  9 return found;
 717    }
 718   
 719  10 public Object visit(ASTCompilationUnit node, Object data) {
 720  10 clearEvalPackages();
 721  10 return super.visit(node, data);
 722    }
 723   
 724  1 public Object visit(ASTEnumDeclaration node, Object data) {
 725    // just skip Enums
 726  1 return data;
 727    }
 728   
 729    /**
 730    * This check must be evaluated independelty for each class. Inner classses
 731    * get their own EvalPackage in order to perform independent evaluation.
 732    */
 733  9 public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
 734  9 if (!node.isInterface()) {
 735  9 return visitClassDec(node, data);
 736    } else {
 737  0 putEvalPackage(nullEvalPackage);
 738  0 Object o = super.visit(node, data);//interface may have inner classes, possible? if not just skip whole interface
 739  0 removeCurrentEvalPackage();
 740  0 return o;
 741    }
 742    }
 743   
 744   
 745    /**
 746    * Non-private constructor's methods are added to a list for later safety
 747    * evaluation. Non-private constructor's calls on private constructors
 748    * are added to a list for later safety evaluation. Private constructors
 749    * are added to a list so their safety to be called can be later evaluated.
 750    * <p/>
 751    * Note: We are not checking private constructor's calls on non-private
 752    * constructors because all non-private constructors will be evaluated for
 753    * safety anyway. This means we wont flag a private constructor as unsafe
 754    * just because it calls an unsafe public constructor. We want to show only
 755    * 1 instance of an error, and this would be 2 instances of the same error.
 756    *
 757    * @todo eliminate the redundency
 758    */
 759  10 public Object visit(ASTConstructorDeclaration node, Object data) {
 760  10 if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {//only evaluate if we have an eval package for this class
 761  10 List calledMethodsOfConstructor = new ArrayList();
 762  10 ConstructorHolder ch = new ConstructorHolder(node);
 763  10 addCalledMethodsOfNode(node, calledMethodsOfConstructor, getCurrentEvalPackage().m_ClassName);
 764  10 if (!node.isPrivate()) {
 765    //these calledMethods are what we will evaluate for being called badly
 766  9 getCurrentEvalPackage().calledMethods.addAll(calledMethodsOfConstructor);
 767    //these called private constructors are what we will evaluate for being called badly
 768    //we add all constructors invoked by non-private constructors
 769    //but we are only interested in the private ones. We just can't tell the difference here
 770  9 ASTExplicitConstructorInvocation eci = ch.getASTExplicitConstructorInvocation();
 771  9 if (eci != null && eci.isThis()) {
 772  1 getCurrentEvalPackage().calledConstructors.add(ch.getCalledConstructor());
 773    }
 774    } else {
 775    //add all private constructors to list for later evaluation on if they are safe to call from another constructor
 776    //store this constructorHolder for later evaluation
 777  1 getCurrentEvalPackage().allPrivateConstructorsOfClass.put(ch, calledMethodsOfConstructor);
 778    }
 779    }
 780  10 return super.visit(node, data);
 781    }
 782   
 783    /**
 784    * Create a MethodHolder to hold the method.
 785    * Store the MethodHolder in the Map as the key
 786    * Store each method called by the current method as a List in the Map as the Object
 787    */
 788  10 public Object visit(ASTMethodDeclarator node, Object data) {
 789  10 if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {//only evaluate if we have an eval package for this class
 790  10 AccessNode parent = (AccessNode) node.jjtGetParent();
 791  10 MethodHolder h = new MethodHolder(node);
 792  10 if (!parent.isAbstract() && !parent.isPrivate() && !parent.isStatic() && !parent.isFinal()) { //Skip abstract methods, have a separate rule for that
 793  7 h.setDangerous();//this method is overridable
 794  7 ASTMethodDeclaration decl = (ASTMethodDeclaration) node.getFirstParentOfType(ASTMethodDeclaration.class);
 795  7 h.setCalledMethod(decl.getMethodName());
 796    }
 797  10 List l = new ArrayList();
 798  10 addCalledMethodsOfNode((SimpleNode) parent, l, getCurrentEvalPackage().m_ClassName);
 799  10 getCurrentEvalPackage().allMethodsOfClass.put(h, l);
 800    }
 801  10 return super.visit(node, data);
 802    }
 803   
 804   
 805  10 private static void addCalledMethodsOfNode(AccessNode node, List calledMethods, String className) {
 806  10 List expressions = new ArrayList();
 807  10 node.findChildrenOfType(ASTPrimaryExpression.class, expressions, false);
 808  10 addCalledMethodsOfNodeImpl(expressions, calledMethods, className);
 809    }
 810   
 811    /**
 812    * Adds all methods called on this instance from within this Node.
 813    */
 814  10 private static void addCalledMethodsOfNode(SimpleNode node, List calledMethods, String className) {
 815  10 List expressions = new ArrayList();
 816  10 node.findChildrenOfType(ASTPrimaryExpression.class, expressions);
 817  10 addCalledMethodsOfNodeImpl(expressions, calledMethods, className);
 818    }
 819   
 820  20 private static void addCalledMethodsOfNodeImpl(List expressions, List calledMethods, String className) {
 821  20 for (Iterator it = expressions.iterator(); it.hasNext();) {
 822  13 ASTPrimaryExpression ape = (ASTPrimaryExpression) it.next();
 823  13 MethodInvocation meth = findMethod(ape, className);
 824  13 if (meth != null) {
 825    //System.out.println("Adding call " + methName);
 826  9 calledMethods.add(meth);
 827    }
 828    }
 829    }
 830   
 831    /**
 832    * @return A method call on the class passed in, or null if no method call
 833    * is found.
 834    * @todo Need a better way to match the class and package name to the actual
 835    * method being called.
 836    */
 837  13 private static MethodInvocation findMethod(ASTPrimaryExpression node, String className) {
 838  13 if (node.jjtGetNumChildren() > 0
 839    && node.jjtGetChild(0).jjtGetNumChildren() > 0
 840    && node.jjtGetChild(0).jjtGetChild(0) instanceof ASTLiteral) {
 841  2 return null;
 842    }
 843  11 MethodInvocation meth = MethodInvocation.getMethod(node);
 844  11 boolean found = false;
 845    // if(meth != null){
 846    // meth.show();
 847    // }
 848  11 if (meth != null) {
 849    //if it's a call on a variable, or on its superclass ignore it.
 850  9 if ((meth.getReferenceNames().size() == 0) && !meth.isSuper()) {
 851    //if this list does not contain our class name, then its not referencing our class
 852    //this is a cheezy test... but it errs on the side of less false hits.
 853  9 List packClass = meth.getQualifierNames();
 854  9 if (packClass.size() > 0) {
 855  0 for (Iterator it = packClass.iterator(); it.hasNext();) {
 856  0 String name = (String) it.next();
 857  0 if (name.equals(className)) {
 858  0 found = true;
 859  0 break;
 860    }
 861    }
 862    } else {
 863  9 found = true;
 864    }
 865    }
 866    }
 867   
 868  11 return found ? meth : null;
 869    }
 870   
 871    /**
 872    * ASTPrimaryPrefix has name in child node of ASTName
 873    */
 874  18 private static String getNameFromPrefix(ASTPrimaryPrefix node) {
 875  18 String name = null;
 876    //should only be 1 child, if more I need more knowledge
 877  18 if (node.jjtGetNumChildren() == 1) { //safety check
 878  18 Node nnode = node.jjtGetChild(0);
 879  18 if (nnode instanceof ASTName) { //just as easy as null check and it should be an ASTName anyway
 880  18 name = ((ASTName) nnode).getImage();
 881    }
 882    }
 883  18 return name;
 884    }
 885   
 886    }