📄 constructorcallsoverridablemethodrule.java
字号:
/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html*/package net.sourceforge.pmd.rules;import net.sourceforge.pmd.RuleContext;import net.sourceforge.pmd.ast.ASTArguments;import net.sourceforge.pmd.ast.ASTClassDeclaration;import net.sourceforge.pmd.ast.ASTCompilationUnit;import net.sourceforge.pmd.ast.ASTConstructorDeclaration;import net.sourceforge.pmd.ast.ASTExplicitConstructorInvocation;import net.sourceforge.pmd.ast.ASTInterfaceDeclaration;import net.sourceforge.pmd.ast.ASTLiteral;import net.sourceforge.pmd.ast.ASTMethodDeclarator;import net.sourceforge.pmd.ast.ASTName;import net.sourceforge.pmd.ast.ASTNestedClassDeclaration;import net.sourceforge.pmd.ast.ASTNestedInterfaceDeclaration;import net.sourceforge.pmd.ast.ASTPrimaryExpression;import net.sourceforge.pmd.ast.ASTPrimaryPrefix;import net.sourceforge.pmd.ast.ASTPrimarySuffix;import net.sourceforge.pmd.ast.ASTUnmodifiedClassDeclaration;import net.sourceforge.pmd.ast.AccessNode;import net.sourceforge.pmd.ast.Node;import net.sourceforge.pmd.ast.SimpleNode;import java.util.ArrayList;import java.util.Collections;import java.util.HashMap;import java.util.Iterator;import java.util.List;import java.util.Map;import java.util.Set;/** * Searches through all methods and constructors called from constructors. It * marks as dangerous any call to overridable methods from non-private * constructors. It marks as dangerous any calls to dangerous private constructors * from non-private constructors. * * * @todo match parameter types. Agressive strips off any package names. Normal * compares the names as is. * * @todo What about interface declarations which can have internal classes * * @author CL Gilbert (dnoyeb@users.sourceforge.net) */public final class ConstructorCallsOverridableMethodRule extends net.sourceforge.pmd.AbstractRule { /** * 2: method(); * ASTPrimaryPrefix * ASTName image = "method" * ASTPrimarySuffix * *ASTArguments * 3: a.method(); * ASTPrimaryPrefix -> * ASTName image = "a.method" ??? * ASTPrimarySuffix -> () * ASTArguments * 3: this.method(); * ASTPrimaryPrefix -> this image=null * ASTPrimarySuffix -> method * ASTPrimarySuffix -> () * ASTArguments * * super.method(); * ASTPrimaryPrefix -> image = "method" * ASTPrimarySuffix -> image = null * ASTArguments -> * * super.a.method(); * ASTPrimaryPrefix -> image = "a" * ASTPrimarySuffix -> image = "method" * ASTPrimarySuffix -> image = null * ASTArguments -> * * 4: this.a.method(); * ASTPrimaryPrefix -> image = null * ASTPrimarySuffix -> image = "a" * ASTPrimarySuffix -> image = "method" * ASTPrimarySuffix -> * ASTArguments * * 4: ClassName.this.method(); * ASTPrimaryPrefix * ASTName image = "ClassName" * ASTPrimarySuffix -> this image=null * ASTPrimarySuffix -> image = "method" * ASTPrimarySuffix -> () * ASTArguments * 5: ClassName.this.a.method(); * ASTPrimaryPrefix * ASTName image = "ClassName" * ASTPrimarySuffix -> this image=null * ASTPrimarySuffix -> image="a" * ASTPrimarySuffix -> image="method" * ASTPrimarySuffix -> () * ASTArguments * 5: Package.ClassName.this.method(); * ASTPrimaryPrefix * ASTName image ="Package.ClassName" * ASTPrimarySuffix -> this image=null * ASTPrimarySuffix -> image="method" * ASTPrimarySuffix -> () * ASTArguments * 6: Package.ClassName.this.a.method(); * ASTPrimaryPrefix * ASTName image ="Package.ClassName" * ASTPrimarySuffix -> this image=null * ASTPrimarySuffix -> a * ASTPrimarySuffix -> method * ASTPrimarySuffix -> () * ASTArguments * 5: OuterClass.InnerClass.this.method(); * ASTPrimaryPrefix * ASTName image = "OuterClass.InnerClass" * ASTPrimarySuffix -> this image=null * ASTPrimarySuffix -> method * ASTPrimarySuffix -> () * ASTArguments * 6: OuterClass.InnerClass.this.a.method(); * ASTPrimaryPrefix * ASTName image = "OuterClass.InnerClass" * ASTPrimarySuffix -> this image=null * ASTPrimarySuffix -> a * ASTPrimarySuffix -> method * ASTPrimarySuffix -> () * ASTArguments * * OuterClass.InnerClass.this.a.method().method().method(); * ASTPrimaryPrefix * ASTName image = "OuterClass.InnerClass" * ASTPrimarySuffix -> this image=null * ASTPrimarySuffix -> a image='a' * ASTPrimarySuffix -> method image='method' * ASTPrimarySuffix -> () image=null * ASTArguments * ASTPrimarySuffix -> method image='method' * ASTPrimarySuffix -> () image=null * ASTArguments * ASTPrimarySuffix -> method image='method' * ASTPrimarySuffix -> () image=null * ASTArguments * * 3..n: Class.InnerClass[0].InnerClass[n].this.method(); * ASTPrimaryPrefix * ASTName image = "Class[0]..InnerClass[n]" * ASTPrimarySuffix -> image=null * ASTPrimarySuffix -> method * ASTPrimarySuffix -> () * ASTArguments * * super.aMethod(); * ASTPrimaryPrefix -> aMethod * ASTPrimarySuffix -> () * * Evaluate right to left * */ private static class MethodInvocation { private String m_Name; private ASTPrimaryExpression m_Ape; private List m_ReferenceNames; private List m_QualifierNames; private int m_ArgumentSize; private boolean m_Super; private MethodInvocation(ASTPrimaryExpression ape, List qualifierNames, List referenceNames, String name, int argumentSize, boolean superCall) { m_Ape = ape; m_QualifierNames = qualifierNames; m_ReferenceNames = referenceNames; m_Name = name; m_ArgumentSize = argumentSize; m_Super = superCall; } public boolean isSuper() { return m_Super; } public String getName() { return m_Name; } public int getArgumentCount() { return m_ArgumentSize; } public List getReferenceNames() { return m_ReferenceNames;//new ArrayList(variableNames); } public List getQualifierNames() { return m_QualifierNames; } public ASTPrimaryExpression getASTPrimaryExpression() { return m_Ape; } public static MethodInvocation getMethod(ASTPrimaryExpression node) { MethodInvocation meth = null; int i = node.jjtGetNumChildren(); if (i > 1) {//should always be at least 2, probably can eliminate this check //start at end which is guaranteed, work backwards Node lastNode = node.jjtGetChild(i - 1); if ((lastNode.jjtGetNumChildren() == 1) && (lastNode.jjtGetChild(0) instanceof ASTArguments)) { //could be ASTExpression for instance 'a[4] = 5'; //start putting method together // System.out.println("Putting method together now"); List varNames = new ArrayList(); List packagesAndClasses = new ArrayList(); //look in JLS for better name here; String methodName = null; ASTArguments args = (ASTArguments) lastNode.jjtGetChild(0); int numOfArguments = args.getArgumentCount(); boolean superFirst = false; int thisIndex = -1; FIND_SUPER_OR_THIS: { //search all nodes except last for 'this' or 'super'. will be at: (node 0 | node 1 | nowhere) //this is an ASTPrimarySuffix with a null image and does not have child (which will be of type ASTArguments) //this is an ASTPrimaryPrefix with a null image and an ASTName that has a null image //super is an ASTPrimarySuffix with a null image and does not have child (which will be of type ASTArguments) //super is an ASTPrimaryPrefix with a non-null image for (int x = 0; x < i - 1; x++) { Node child = node.jjtGetChild(x); if (child instanceof ASTPrimarySuffix) { //check suffix type match ASTPrimarySuffix child2 = (ASTPrimarySuffix) child; // String name = getNameFromSuffix((ASTPrimarySuffix)child); // System.out.println("found name suffix of : " + name); if (child2.getImage() == null && child2.jjtGetNumChildren() == 0) { thisIndex = x; break; } //could be super, could be this. currently we cant tell difference so we miss super when //XYZ.ClassName.super.method(); //still works though. } else if (child instanceof ASTPrimaryPrefix) { //check prefix type match ASTPrimaryPrefix child2 = (ASTPrimaryPrefix) child; if (getNameFromPrefix(child2) == null) { if (child2.getImage() == null) { thisIndex = x; break; } else {//happens when super is used [super.method(): image = 'method'] superFirst = true; thisIndex = x; //the true super is at an unusable index because super.method() has only 2 nodes [method=0,()=1] //as opposed to the 3 you might expect and which this.method() actually has. [this=0,method=1.()=2] break; } } } // else{ // System.err.println("Bad Format error"); //throw exception, quit evaluating this compilation node // } } } if (thisIndex != -1) { // System.out.println("Found this or super: " + thisIndex); //Hack that must be removed if and when the patters of super.method() begins to logically match the rest of the patterns !!! if (superFirst) { //this is when super is the first node of statement. no qualifiers, all variables or method // System.out.println("super first"); FIRSTNODE:{ ASTPrimaryPrefix child = (ASTPrimaryPrefix) node.jjtGetChild(0); String name = child.getImage();//special case if (i == 2) { //last named node = method name methodName = name; } else { //not the last named node so its only var name varNames.add(name); } } OTHERNODES:{ //variables for (int x = 1; x < i - 1; x++) { Node child = node.jjtGetChild(x); ASTPrimarySuffix ps = (ASTPrimarySuffix) child; if (ps.isArguments() == false) { String name = ((ASTPrimarySuffix) child).getImage(); if (x == i - 2) {//last node methodName = name; } else {//not the last named node so its only var name varNames.add(name); } } } } } else {//not super call FIRSTNODE:{ if (thisIndex == 1) {//qualifiers in node 0 ASTPrimaryPrefix child = (ASTPrimaryPrefix) node.jjtGetChild(0); String toParse = getNameFromPrefix(child); // System.out.println("parsing for class/package names in : " + toParse); java.util.StringTokenizer st = new java.util.StringTokenizer(toParse, "."); while (st.hasMoreTokens()) { packagesAndClasses.add(st.nextToken()); } } } OTHERNODES:{ //other methods called in this statement are grabbed here //this is at 0, then no Qualifiers //this is at 1, the node 0 contains qualifiers for (int x = thisIndex + 1; x < i - 1; x++) {//everything after this is var name or method name ASTPrimarySuffix child = (ASTPrimarySuffix) node.jjtGetChild(x); if (child.isArguments() == false) { //skip the () of method calls String name = getNameFromSuffix(child); // System.out.println("Found suffix: " + suffixName); if (x == i - 2) { methodName = name; } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -