1 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 43 public final class ConstructorCallsOverridableMethod extends AbstractRule { 44 155 private static class MethodInvocation { 156 private String m_Name; 157 private ASTPrimaryExpression m_Ape; 158 private List m_ReferenceNames; 159 private List m_QualifierNames; 160 private int m_ArgumentSize; 161 private boolean m_Super; 162 163 private MethodInvocation(ASTPrimaryExpression ape, List qualifierNames, List referenceNames, String name, int argumentSize, boolean superCall) { 164 m_Ape = ape; 165 m_QualifierNames = qualifierNames; 166 m_ReferenceNames = referenceNames; 167 m_Name = name; 168 m_ArgumentSize = argumentSize; 169 m_Super = superCall; 170 } 171 172 public boolean isSuper() { 173 return m_Super; 174 } 175 176 public String getName() { 177 return m_Name; 178 } 179 180 public int getArgumentCount() { 181 return m_ArgumentSize; 182 } 183 184 public List getReferenceNames() { 185 return m_ReferenceNames; } 187 188 public List getQualifierNames() { 189 return m_QualifierNames; 190 } 191 192 public ASTPrimaryExpression getASTPrimaryExpression() { 193 return m_Ape; 194 } 195 196 public static MethodInvocation getMethod(ASTPrimaryExpression node) { 197 MethodInvocation meth = null; 198 int i = node.jjtGetNumChildren(); 199 if (i > 1) { Node lastNode = node.jjtGetChild(i - 1); 202 if ((lastNode.jjtGetNumChildren() == 1) && (lastNode.jjtGetChild(0) instanceof ASTArguments)) { List varNames = new ArrayList (); 206 List packagesAndClasses = new ArrayList (); String methodName = null; 208 ASTArguments args = (ASTArguments) lastNode.jjtGetChild(0); 209 int numOfArguments = args.getArgumentCount(); 210 boolean superFirst = false; 211 int thisIndex = -1; 212 213 FIND_SUPER_OR_THIS: { 214 for (int x = 0; x < i - 1; x++) { 220 Node child = node.jjtGetChild(x); 221 if (child instanceof ASTPrimarySuffix) { ASTPrimarySuffix child2 = (ASTPrimarySuffix) child; 223 if (child2.getImage() == null && child2.jjtGetNumChildren() == 0) { 226 thisIndex = x; 227 break; 228 } 229 } else if (child instanceof ASTPrimaryPrefix) { ASTPrimaryPrefix child2 = (ASTPrimaryPrefix) child; 234 if (getNameFromPrefix(child2) == null) { 235 if (child2.getImage() == null) { 236 thisIndex = x; 237 break; 238 } else { superFirst = true; 240 thisIndex = x; 241 break; 244 } 245 } 246 } 247 } 251 } 252 253 if (thisIndex != -1) { 254 if (superFirst) { FIRSTNODE:{ 259 ASTPrimaryPrefix child = (ASTPrimaryPrefix) node.jjtGetChild(0); 260 String name = child.getImage(); if (i == 2) { methodName = name; 263 } else { varNames.add(name); 265 } 266 } 267 OTHERNODES:{ 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 name = ((ASTPrimarySuffix) child).getImage(); 273 if (x == i - 2) { methodName = name; 275 } else { varNames.add(name); 277 } 278 } 279 } 280 } 281 } else { FIRSTNODE:{ 283 if (thisIndex == 1) { ASTPrimaryPrefix child = (ASTPrimaryPrefix) node.jjtGetChild(0); 285 String toParse = getNameFromPrefix(child); 286 java.util.StringTokenizer st = new java.util.StringTokenizer (toParse, "."); 288 while (st.hasMoreTokens()) { 289 packagesAndClasses.add(st.nextToken()); 290 } 291 } 292 } 293 OTHERNODES:{ for (int x = thisIndex + 1; x < i - 1; x++) { ASTPrimarySuffix child = (ASTPrimarySuffix) node.jjtGetChild(x); 298 if (!child.isArguments()) { String name = child.getImage(); 300 if (x == i - 2) { 302 methodName = name; 303 } else { 304 varNames.add(name); 305 } 306 } 307 } 308 } 309 } 310 } else { FIRSTNODE:{ ASTPrimaryPrefix child = (ASTPrimaryPrefix) node.jjtGetChild(0); 314 String toParse = getNameFromPrefix(child); 315 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) { methodName = value; 322 } else { 323 varNames.add(value); 324 } 325 } else { varNames.add(value); 327 } 328 } 329 } 330 OTHERNODES:{ for (int x = 1; x < i - 1; x++) { 332 ASTPrimarySuffix child = (ASTPrimarySuffix) node.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 List pkg = getQualifierNames(); 353 System.out.println(" <Qualifiers>"); 354 for (Iterator it = pkg.iterator(); it.hasNext();) { 355 String name = (String ) it.next(); 356 System.out.println(" " + name); 357 } 358 System.out.println(" </Qualifiers>"); 359 System.out.println(" <Super>" + isSuper() + "</Super>"); 360 List vars = getReferenceNames(); 361 System.out.println(" <References>"); 362 for (Iterator it = vars.iterator(); it.hasNext();) { 363 String name = (String ) it.next(); 364 System.out.println(" " + name); 365 } 366 System.out.println(" </References>"); 367 System.out.println(" <Name>" + getName() + "</Name>"); 368 System.out.println("</MethodInvocation>"); 369 } 370 } 371 372 private static final class ConstructorInvocation { 373 private ASTExplicitConstructorInvocation m_Eci; 374 private String name; 375 private int count = 0; 376 377 public ConstructorInvocation(ASTExplicitConstructorInvocation eci) { 378 m_Eci = eci; 379 List l = new ArrayList (); 380 eci.findChildrenOfType(ASTArguments.class, l); 381 if (!l.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 getName() { 397 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 public MethodHolder(ASTMethodDeclarator amd) { 407 this.amd = amd; 408 } 409 410 public void setCalledMethod(String name) { 411 this.called = name; 412 } 413 414 public String 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 expressions = new ArrayList (); 465 m_Cd.findChildrenOfType(ASTExplicitConstructorInvocation.class, expressions); if (!expressions.isEmpty()) { 467 ASTExplicitConstructorInvocation eci = (ASTExplicitConstructorInvocation) expressions.get(0); 468 m_Ci = new ConstructorInvocation(eci); 469 } 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 486 private static class EvalPackage { 487 public EvalPackage() { 488 } 489 490 public EvalPackage(String className) { 491 m_ClassName = className; 492 calledMethods = new ArrayList (); allMethodsOfClass = new HashMap (); 494 calledConstructors = new ArrayList (); 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 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 522 private final List evalPackages = new ArrayList (); 524 private EvalPackage getCurrentEvalPackage() { 525 return (EvalPackage) evalPackages.get(evalPackages.size() - 1); 526 } 527 528 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 547 private Object visitClassDec(ASTClassOrInterfaceDeclaration node, Object data) { 548 String className = node.getImage(); 549 if (!node.isFinal() && !node.isStatic()) { 550 putEvalPackage(new EvalPackage(className)); 551 } else { 552 putEvalPackage(nullEvalPackage); 553 } 554 super.visit((ASTClassOrInterfaceDeclaration) node, data); 556 557 if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) { 559 while (evaluateDangerOfMethods(getCurrentEvalPackage().allMethodsOfClass)) { 561 } 562 evaluateDangerOfConstructors1(getCurrentEvalPackage().allPrivateConstructorsOfClass, getCurrentEvalPackage().allMethodsOfClass.keySet()); 564 while (evaluateDangerOfConstructors2(getCurrentEvalPackage().allPrivateConstructorsOfClass)) { 565 } 566 567 for (Iterator it = getCurrentEvalPackage().calledMethods.iterator(); it.hasNext();) { 569 MethodInvocation meth = (MethodInvocation) it.next(); 570 for (Iterator it2 = getCurrentEvalPackage().allMethodsOfClass.keySet().iterator(); it2.hasNext();) { 572 MethodHolder h = (MethodHolder) it2.next(); 573 if (h.isDangerous()) { 574 String 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 for (Iterator privConstIter = getCurrentEvalPackage().allPrivateConstructorsOfClass.keySet().iterator(); privConstIter.hasNext();) { 584 ConstructorHolder ch = (ConstructorHolder) privConstIter.next(); 585 if (ch.isDangerous()) { int paramCount = ch.getASTConstructorDeclaration().getParameterCount(); 588 for (Iterator calledConstIter = getCurrentEvalPackage().calledConstructors.iterator(); calledConstIter.hasNext();) { 589 ConstructorInvocation ci = (ConstructorInvocation) calledConstIter.next(); 590 if (ci.getArgumentCount() == paramCount) { 591 addViolation(data, ci.getASTExplicitConstructorInvocation(), "constructor"); 593 } 594 } 595 } 596 } 597 } 598 removeCurrentEvalPackage(); 600 return data; 601 } 602 603 617 private boolean evaluateDangerOfMethods(Map classMethodMap) { 618 boolean found = false; 620 for (Iterator methodsIter = classMethodMap.entrySet().iterator(); methodsIter.hasNext();) { 621 Map.Entry entry = (Map.Entry ) methodsIter.next(); 622 623 MethodHolder h = (MethodHolder) entry.getKey(); 624 List calledMeths = (List ) entry.getValue(); 625 for (Iterator calledMethsIter = calledMeths.iterator(); calledMethsIter.hasNext() && !h.isDangerous();) { 626 MethodInvocation meth = (MethodInvocation) calledMethsIter.next(); 628 for (Iterator innerMethsIter = classMethodMap.keySet().iterator(); innerMethsIter.hasNext();) { MethodHolder h3 = (MethodHolder) innerMethsIter.next(); 631 if (h3.isDangerous()) { 632 String matchMethodName = h3.getASTMethodDeclarator().getImage(); 633 int matchMethodParamCount = h3.getASTMethodDeclarator().getParameterCount(); 634 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 654 private void evaluateDangerOfConstructors1(Map classConstructorMap, Set evaluatedMethods) { 655 for (Iterator constIter = classConstructorMap.entrySet().iterator(); constIter.hasNext();) { 657 Map.Entry entry = (Map.Entry ) constIter.next(); 658 ConstructorHolder ch = (ConstructorHolder) entry.getKey(); 659 if (!ch.isDangerous()) { List calledMeths = (List ) entry.getValue(); 662 for (Iterator calledMethsIter = calledMeths.iterator(); calledMethsIter.hasNext() && !ch.isDangerous();) { MethodInvocation meth = (MethodInvocation) calledMethsIter.next(); String methName = meth.getName(); 666 int methArgCount = meth.getArgumentCount(); 667 for (Iterator evaldMethsIter = evaluatedMethods.iterator(); evaldMethsIter.hasNext();) { 669 MethodHolder h = (MethodHolder) evaldMethsIter.next(); 670 if (h.isDangerous()) { 671 String matchName = h.getASTMethodDeclarator().getImage(); 672 int matchParamCount = h.getASTMethodDeclarator().getParameterCount(); 673 if (methName.equals(matchName) && (methArgCount == matchParamCount)) { 674 ch.setDangerous(true); 675 break; 677 } 678 } 679 680 } 681 } 682 } 683 } 684 } 685 686 695 private boolean evaluateDangerOfConstructors2(Map classConstructorMap) { 696 boolean found = false; for (Iterator 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 int cCount = calledC.getArgumentCount(); 707 for (Iterator innerConstIter = classConstructorMap.keySet().iterator(); innerConstIter.hasNext() && !ch.isDangerous();) { 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 } 716 } 717 } 718 } 719 return found; 720 } 721 722 public Object visit(ASTCompilationUnit node, Object data) { 723 clearEvalPackages(); 724 return super.visit(node, data); 725 } 726 727 public Object visit(ASTEnumDeclaration node, Object data) { 728 return data; 730 } 731 732 736 public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { 737 if (!node.isInterface()) { 738 return visitClassDec(node, data); 739 } else { 740 putEvalPackage(nullEvalPackage); 741 Object o = super.visit(node, data); removeCurrentEvalPackage(); 743 return o; 744 } 745 } 746 747 748 762 public Object visit(ASTConstructorDeclaration node, Object data) { 763 if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) { List calledMethodsOfConstructor = new ArrayList (); 765 ConstructorHolder ch = new ConstructorHolder(node); 766 addCalledMethodsOfNode(node, calledMethodsOfConstructor, getCurrentEvalPackage().m_ClassName); 767 if (!node.isPrivate()) { 768 getCurrentEvalPackage().calledMethods.addAll(calledMethodsOfConstructor); 770 ASTExplicitConstructorInvocation eci = ch.getASTExplicitConstructorInvocation(); 774 if (eci != null && eci.isThis()) { 775 getCurrentEvalPackage().calledConstructors.add(ch.getCalledConstructor()); 776 } 777 } else { 778 getCurrentEvalPackage().allPrivateConstructorsOfClass.put(ch, calledMethodsOfConstructor); 781 } 782 } 783 return super.visit(node, data); 784 } 785 786 791 public Object visit(ASTMethodDeclarator node, Object data) { 792 if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) { AccessNode parent = (AccessNode) node.jjtGetParent(); 794 MethodHolder h = new MethodHolder(node); 795 if (!parent.isAbstract() && !parent.isPrivate() && !parent.isStatic() && !parent.isFinal()) { h.setDangerous(); ASTMethodDeclaration decl = (ASTMethodDeclaration) node.getFirstParentOfType(ASTMethodDeclaration.class); 798 h.setCalledMethod(decl.getMethodName()); 799 } 800 List l = new ArrayList (); 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 calledMethods, String className) { 809 List expressions = new ArrayList (); 810 node.findChildrenOfType(ASTPrimaryExpression.class, expressions, false); 811 addCalledMethodsOfNodeImpl(expressions, calledMethods, className); 812 } 813 814 817 private static void addCalledMethodsOfNode(SimpleNode node, List calledMethods, String className) { 818 List expressions = new ArrayList (); 819 node.findChildrenOfType(ASTPrimaryExpression.class, expressions); 820 addCalledMethodsOfNodeImpl(expressions, calledMethods, className); 821 } 822 823 private static void addCalledMethodsOfNodeImpl(List expressions, List calledMethods, String className) { 824 for (Iterator it = expressions.iterator(); it.hasNext();) { 825 ASTPrimaryExpression ape = (ASTPrimaryExpression) it.next(); 826 MethodInvocation meth = findMethod(ape, className); 827 if (meth != null) { 828 calledMethods.add(meth); 830 } 831 } 832 } 833 834 840 private static MethodInvocation findMethod(ASTPrimaryExpression node, String 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) { 852 if ((meth.getReferenceNames().size() == 0) && !meth.isSuper()) { 854 List packClass = meth.getQualifierNames(); 857 if (!packClass.isEmpty()) { 858 for (Iterator it = packClass.iterator(); it.hasNext();) { 859 String name = (String ) 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 877 private static String getNameFromPrefix(ASTPrimaryPrefix node) { 878 String name = null; 879 if (node.jjtGetNumChildren() == 1) { Node nnode = node.jjtGetChild(0); 882 if (nnode instanceof ASTName) { name = ((ASTName) nnode).getImage(); 884 } 885 } 886 return name; 887 } 888 889 } 890 | Popular Tags |