0001: /*
0002: * ConstructorCallsOverridableMethodRule.java
0003: * dnoyeb@users.sourceforge.net
0004: * Created on February 5, 2003, 1:54 PM
0005: */
0006: package org.acm.seguin.pmd.rules;
0007:
0008: import org.acm.seguin.pmd.RuleContext;
0009: import net.sourceforge.jrefactory.ast.ASTArguments;
0010: import net.sourceforge.jrefactory.ast.ASTClassDeclaration;
0011: import net.sourceforge.jrefactory.ast.ASTCompilationUnit;
0012: import net.sourceforge.jrefactory.ast.ASTConstructorDeclaration;
0013: import net.sourceforge.jrefactory.ast.ASTExplicitConstructorInvocation;
0014: import net.sourceforge.jrefactory.ast.ASTInterfaceDeclaration;
0015: import net.sourceforge.jrefactory.ast.ASTLiteral;
0016: import net.sourceforge.jrefactory.ast.ASTMethodDeclarator;
0017: import net.sourceforge.jrefactory.ast.ASTName;
0018: import net.sourceforge.jrefactory.ast.ASTNestedClassDeclaration;
0019: import net.sourceforge.jrefactory.ast.ASTNestedInterfaceDeclaration;
0020: import net.sourceforge.jrefactory.ast.ASTPrimaryExpression;
0021: import net.sourceforge.jrefactory.ast.ASTPrimaryPrefix;
0022: import net.sourceforge.jrefactory.ast.ASTPrimarySuffix;
0023: import net.sourceforge.jrefactory.ast.ASTUnmodifiedClassDeclaration;
0024: import net.sourceforge.jrefactory.ast.ASTExpression;
0025: import net.sourceforge.jrefactory.ast.AccessNode;
0026: import net.sourceforge.jrefactory.ast.Node;
0027: import net.sourceforge.jrefactory.ast.SimpleNode;
0028:
0029: import java.util.ArrayList;
0030: import java.util.Collections;
0031: import java.util.HashMap;
0032: import java.util.Iterator;
0033: import java.util.List;
0034: import java.util.Map;
0035: import java.util.Set;
0036:
0037: /**
0038: * Searches through all methods and constructors called from constructors. It marks as dangerous any call to
0039: * overridable methods from non-private constructors. It marks as dangerous any calls to dangerous private constructors
0040: * from non-private constructors.
0041: *
0042: *@author CL Gilbert (dnoyeb@users.sourceforge.net)
0043: *@todo match parameter types. Agressive strips off any package names. Normal compares the names as is.
0044: *@todo What about interface declarations which can have internal classes
0045: */
0046: public final class ConstructorCallsOverridableMethodRule extends
0047: org.acm.seguin.pmd.AbstractRule {
0048:
0049: /** 1 package per class. */
0050: private final List evalPackages = new ArrayList();
0051:
0052: private final static NullEvalPackage nullEvalPackage = new NullEvalPackage();
0053:
0054: ////////////////////////////////////////////////////////////////////////////////
0055: ////////////////////////////////////////////////////////////////////////////////
0056: ////////////////////////////////////////////////////////////////////////////////
0057: //The Visited Methods
0058:
0059: /**
0060: * Work on each file independently.
0061: *
0062: *@param node Description of Parameter
0063: *@param data Description of Parameter
0064: *@return Description of the Returned Value
0065: */
0066: public Object visit(ASTCompilationUnit node, Object data) {
0067: clearEvalPackages();
0068: // try {
0069: return super .visit(node, data);
0070: // }
0071: // catch(Exception e){
0072: // e.printStackTrace();
0073: // }
0074: }
0075:
0076: //for testing only
0077:
0078: // public Object visit(ASTPackageDeclaration node, Object data){
0079: // System.out.println("package= " + ((ASTName)node.jjtGetFirstChild()).getImage());
0080: // return super.visit(node,data);
0081: // }
0082:
0083: /**
0084: * This check must be evaluated independelty for each class. Inner classses get their own EvalPackage in order to
0085: * perform independent evaluation.
0086: *
0087: *@param node Description of Parameter
0088: *@param data Description of Parameter
0089: *@return Description of the Returned Value
0090: */
0091: public Object visit(ASTClassDeclaration node, Object data) {
0092: return visitClassDec(node, data);
0093: }
0094:
0095: /**
0096: * Description of the Method
0097: *
0098: *@param node Description of Parameter
0099: *@param data Description of Parameter
0100: *@return Description of the Returned Value
0101: */
0102: public Object visit(ASTNestedClassDeclaration node, Object data) {
0103: return visitClassDec(node, data);
0104: }
0105:
0106: /**
0107: * Description of the Method
0108: *
0109: *@param node Description of Parameter
0110: *@param data Description of Parameter
0111: *@return Description of the Returned Value
0112: */
0113: public Object visit(ASTInterfaceDeclaration node, Object data) {
0114: putEvalPackage(nullEvalPackage);
0115: Object o = super .visit(node, data);
0116: //interface may have inner classes, possible? if not just skip whole interface
0117: removeCurrentEvalPackage();
0118: return o;
0119: }
0120:
0121: /**
0122: * Description of the Method
0123: *
0124: *@param node Description of Parameter
0125: *@param data Description of Parameter
0126: *@return Description of the Returned Value
0127: */
0128: public Object visit(ASTNestedInterfaceDeclaration node, Object data) {
0129: putEvalPackage(nullEvalPackage);
0130: Object o = super .visit(node, data);
0131: //interface may have inner classes, possible? if not just skip whole interface
0132: removeCurrentEvalPackage();
0133: return o;
0134: }
0135:
0136: /**
0137: * Non-private constructor's methods are added to a list for later safety evaluation. Non-private constructor's
0138: * calls on private constructors are added to a list for later safety evaluation. Private constructors are added to
0139: * a list so their safety to be called can be later evaluated. Note: We are not checking private constructor's calls
0140: * on non-private constructors because all non-private constructors will be evaluated for safety anyway. This means
0141: * we wont flag a private constructor as unsafe just because it calls an unsafe public constructor. We want to show
0142: * only 1 instance of an error, and this would be 2 instances of the same error.
0143: *
0144: *@param node Description of Parameter
0145: *@param data Description of Parameter
0146: *@return Description of the Returned Value
0147: *@todo eliminate the redundency
0148: */
0149: public Object visit(ASTConstructorDeclaration node, Object data) {
0150: if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {
0151: //only evaluate if we have an eval package for this class
0152: List calledMethodsOfConstructor = new ArrayList();
0153: ConstructorHolder ch = new ConstructorHolder(node);
0154: addCalledMethodsOfNode(node, calledMethodsOfConstructor,
0155: getCurrentEvalPackage().m_ClassName);
0156: if (!node.isPrivate()) {
0157: //these calledMethods are what we will evaluate for being called badly
0158: getCurrentEvalPackage().calledMethods
0159: .addAll(calledMethodsOfConstructor);
0160: //these called private constructors are what we will evaluate for being called badly
0161: //we add all constructors invoked by non-private constructors
0162: //but we are only interested in the private ones. We just can't tell the difference here
0163: ASTExplicitConstructorInvocation eci = ch
0164: .getASTExplicitConstructorInvocation();
0165: if (eci != null && "this".equals(eci.getImage())) {
0166: //&& eci.isThis()) {
0167: getCurrentEvalPackage().calledConstructors.add(ch
0168: .getCalledConstructor());
0169: }
0170: } else {
0171: //add all private constructors to list for later evaluation on if they are safe to call from another constructor
0172: //store this constructorHolder for later evaluation
0173: getCurrentEvalPackage().allPrivateConstructorsOfClass
0174: .put(ch, calledMethodsOfConstructor);
0175: }
0176: }
0177: return super .visit(node, data);
0178: }
0179:
0180: /**
0181: * Create a MethodHolder to hold the method. Store the MethodHolder in the Map as the key Store each method called
0182: * by the current method as a List in the Map as the Object
0183: *
0184: *@param node Description of Parameter
0185: *@param data Description of Parameter
0186: *@return Description of the Returned Value
0187: */
0188: public Object visit(ASTMethodDeclarator node, Object data) {
0189: if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {
0190: //only evaluate if we have an eval package for this class
0191: AccessNode parent = (AccessNode) node.jjtGetParent();
0192: MethodHolder h = new MethodHolder(node);
0193: if (!parent.isPrivate() && !parent.isStatic()
0194: && !parent.isFinal()) {
0195: h.setDangerous(true);
0196: //this method is overridable
0197: }
0198: List l = new ArrayList();
0199: addCalledMethodsOfNode((SimpleNode) parent, l,
0200: getCurrentEvalPackage().m_ClassName);
0201: getCurrentEvalPackage().allMethodsOfClass.put(h, l);
0202: }
0203: return super .visit(node, data);
0204: }
0205:
0206: //could use java.util.Stack
0207:
0208: /**
0209: * Gets the CurrentEvalPackage attribute of the ConstructorCallsOverridableMethodRule object
0210: *
0211: *@return The CurrentEvalPackage value
0212: */
0213: private EvalPackage getCurrentEvalPackage() {
0214: return (EvalPackage) evalPackages.get(evalPackages.size() - 1);
0215: }
0216:
0217: /**
0218: * Adds and evaluation package and makes it current
0219: *
0220: *@param ep Description of Parameter
0221: */
0222: private void putEvalPackage(EvalPackage ep) {
0223: evalPackages.add(ep);
0224: }
0225:
0226: /** Description of the Method */
0227: private void removeCurrentEvalPackage() {
0228: evalPackages.remove(evalPackages.size() - 1);
0229: }
0230:
0231: /** Description of the Method */
0232: private void clearEvalPackages() {
0233: evalPackages.clear();
0234: }
0235:
0236: /**
0237: * This check must be evaluated independelty for each class. Inner classses get their own EvalPackage in order to
0238: * perform independent evaluation.
0239: *
0240: *@param node Description of Parameter
0241: *@param data Description of Parameter
0242: *@return Description of the Returned Value
0243: */
0244: private Object visitClassDec(AccessNode node, Object data) {
0245: String className = ((ASTUnmodifiedClassDeclaration) node
0246: .jjtGetFirstChild()).getImage();
0247: // System.out.println("Class is " + className);
0248: //evaluate each level independently
0249: if (!node.isFinal() && !node.isStatic()) {
0250: putEvalPackage(new EvalPackage(className));
0251: } else {
0252: //System.out.println("NullEvalPackage!");
0253: putEvalPackage(nullEvalPackage);
0254: }
0255: //store any errors caught from other passes.
0256: if (node instanceof ASTClassDeclaration) {
0257: super .visit((ASTClassDeclaration) node, data);
0258: } else {
0259: super .visit((ASTNestedClassDeclaration) node, data);
0260: }
0261: //skip this class if it has no evaluation package
0262: if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {
0263: //evaluate danger of all methods in class
0264: while (evaluateDangerOfMethods(getCurrentEvalPackage().allMethodsOfClass) == true) {
0265: ;
0266: }
0267: //evaluate danger of constructors
0268: evaluateDangerOfConstructors1(
0269: getCurrentEvalPackage().allPrivateConstructorsOfClass,
0270: getCurrentEvalPackage().allMethodsOfClass.keySet());
0271: while (evaluateDangerOfConstructors2(getCurrentEvalPackage().allPrivateConstructorsOfClass) == true) {
0272: ;
0273: }
0274:
0275: //get each method called on this object from a non-private constructor, if its dangerous flag it
0276: for (Iterator it = getCurrentEvalPackage().calledMethods
0277: .iterator(); it.hasNext();) {
0278: MethodInvocation meth = (MethodInvocation) it.next();
0279: //check against each dangerous method in class
0280: for (Iterator it2 = getCurrentEvalPackage().allMethodsOfClass
0281: .keySet().iterator(); it2.hasNext();) {
0282: MethodHolder h = (MethodHolder) it2.next();
0283: if (h.isDangerous()) {
0284: String methName = h.getASTMethodDeclarator()
0285: .getImage();
0286: int count = h.getASTMethodDeclarator()
0287: .getParameterCount();
0288: if (meth.getName().equals(methName)
0289: && (meth.getArgumentCount() == count)) {
0290: //bad call
0291: RuleContext ctx = (RuleContext) data;
0292: ctx.getReport().addRuleViolation(
0293: createRuleViolation(ctx, meth
0294: .getASTPrimaryExpression()
0295: .getBeginLine()));
0296: }
0297: }
0298: }
0299: }
0300: //get each unsafe private constructor, and check if its called from any non private constructors
0301: for (Iterator privConstIter = getCurrentEvalPackage().allPrivateConstructorsOfClass
0302: .keySet().iterator(); privConstIter.hasNext();) {
0303: ConstructorHolder ch = (ConstructorHolder) privConstIter
0304: .next();
0305: if (ch.isDangerous()) {
0306: //if its dangerous check if its called from any non-private constructors
0307: //System.out.println("visitClassDec Evaluating dangerous constructor with " + ch.getASTConstructorDeclaration().getParameterCount() + " params");
0308: int paramCount = ch.getASTConstructorDeclaration()
0309: .getParameterCount();
0310: for (Iterator calledConstIter = getCurrentEvalPackage().calledConstructors
0311: .iterator(); calledConstIter.hasNext();) {
0312: ConstructorInvocation ci = (ConstructorInvocation) calledConstIter
0313: .next();
0314: //System.out.println("called constructor ci.getArgumentCount()="+ci.getArgumentCount());
0315: if (ci.getArgumentCount() == paramCount) {
0316: //match name super / this !?
0317: //System.out.println("adding report");
0318: RuleContext ctx = (RuleContext) data;
0319: ctx
0320: .getReport()
0321: .addRuleViolation(
0322: createRuleViolation(
0323: ctx,
0324: ci
0325: .getASTExplicitConstructorInvocation()
0326: .getBeginLine()));
0327: }
0328: }
0329: }
0330: }
0331: }
0332: //finished evaluating this class, move up a level
0333: removeCurrentEvalPackage();
0334: return data;
0335: }
0336:
0337: /**
0338: * Check the methods called on this class by each of the methods on this class. If a method calls an unsafe method,
0339: * mark the calling method as unsafe. This changes the list of unsafe methods which necessitates another pass. Keep
0340: * passing until you make a clean pass in which no methods are changed to unsafe. For speed it is possible to limit
0341: * the number of passes. Impossible to tell type of arguments to method, so forget method matching on types. just
0342: * use name and num of arguments. will be some false hits, but oh well.
0343: *
0344: *@param classMethodMap Description of Parameter
0345: *@return Description of the Returned Value
0346: *@todo investigate limiting the number of passes through config.
0347: */
0348: private boolean evaluateDangerOfMethods(Map classMethodMap) {
0349: //check each method if it calls overridable method
0350: boolean found = false;
0351: for (Iterator methodsIter = classMethodMap.keySet().iterator(); methodsIter
0352: .hasNext();) {
0353: MethodHolder h = (MethodHolder) methodsIter.next();
0354: List calledMeths = (List) classMethodMap.get(h);
0355: for (Iterator calledMethsIter = calledMeths.iterator(); calledMethsIter
0356: .hasNext()
0357: && (h.isDangerous() == false);) {
0358: //if this method matches one of our dangerous methods, mark it dangerous
0359: MethodInvocation meth = (MethodInvocation) calledMethsIter
0360: .next();
0361: //System.out.println("Called meth is " + meth);
0362: for (Iterator innerMethsIter = classMethodMap.keySet()
0363: .iterator(); innerMethsIter.hasNext();) {
0364: //need to skip self here h == h3
0365: MethodHolder h3 = (MethodHolder) innerMethsIter
0366: .next();
0367: if (h3.isDangerous()) {
0368: String matchMethodName = h3
0369: .getASTMethodDeclarator().getImage();
0370: int matchMethodParamCount = h3
0371: .getASTMethodDeclarator()
0372: .getParameterCount();
0373: //System.out.println("matchint " + matchMethodName + " to " + meth.getName());
0374: if (matchMethodName.equals(meth.getName())
0375: && (matchMethodParamCount == meth
0376: .getArgumentCount())) {
0377: h.setDangerous(true);
0378: found = true;
0379: break;
0380: }
0381: }
0382: }
0383: }
0384: }
0385: return found;
0386: }
0387:
0388: /**
0389: * marks constructors dangerous if they call any dangerous methods Requires only a single pass as methods are
0390: * already marked
0391: *
0392: *@param classConstructorMap Description of Parameter
0393: *@param evaluatedMethods Description of Parameter
0394: *@todo optimize by having methods already evaluated somehow!?
0395: */
0396: private void evaluateDangerOfConstructors1(Map classConstructorMap,
0397: Set evaluatedMethods) {
0398: //check each constructor in the class
0399: for (Iterator constIter = classConstructorMap.keySet()
0400: .iterator(); constIter.hasNext();) {
0401: ConstructorHolder ch = (ConstructorHolder) constIter.next();
0402: if (!ch.isDangerous()) {
0403: //if its not dangerous then evaluate if it should be
0404: //if it calls dangerous method mark it as dangerous
0405: List calledMeths = (List) classConstructorMap.get(ch);
0406: //check each method it calls
0407: for (Iterator calledMethsIter = calledMeths.iterator(); calledMethsIter
0408: .hasNext()
0409: && !ch.isDangerous();) {
0410: //but thee are diff objects which represent same thing but were never evaluated, they need reevaluation
0411: MethodInvocation meth = (MethodInvocation) calledMethsIter
0412: .next();
0413: //CCE
0414: String methName = meth.getName();
0415: int methArgCount = meth.getArgumentCount();
0416: //check each of the already evaluated methods: need to optimize this out
0417: for (Iterator evaldMethsIter = evaluatedMethods
0418: .iterator(); evaldMethsIter.hasNext();) {
0419: MethodHolder h = (MethodHolder) evaldMethsIter
0420: .next();
0421: if (h.isDangerous()) {
0422: String matchName = h
0423: .getASTMethodDeclarator()
0424: .getImage();
0425: int matchParamCount = h
0426: .getASTMethodDeclarator()
0427: .getParameterCount();
0428: if (methName.equals(matchName)
0429: && (methArgCount == matchParamCount)) {
0430: ch.setDangerous(true);
0431: //System.out.println("evaluateDangerOfConstructors1 setting dangerous constructor with " + ch.getASTConstructorDeclaration().getParameterCount() + " params");
0432: break;
0433: }
0434: }
0435: }
0436: }
0437: }
0438: }
0439: }
0440:
0441: /**
0442: * Constructor map should contain a key for each private constructor, and maps to a List which contains all called
0443: * constructors of that key. marks dangerous if call dangerous private constructor we ignore all non-private
0444: * constructors here. That is, the map passed in should not contain any non-private constructors. we return boolean
0445: * in order to limit the number of passes through this method but it seems as if we can forgo that and just process
0446: * it till its done.
0447: *
0448: *@param classConstructorMap Description of Parameter
0449: *@return Description of the Returned Value
0450: */
0451: private boolean evaluateDangerOfConstructors2(
0452: Map classConstructorMap) {
0453: //System.out.println("evaluateDangerOfConstructors2 map="+classConstructorMap);
0454: boolean found = false;
0455: //triggers on danger state change
0456: //check each constructor in the class
0457: for (Iterator constIter = classConstructorMap.keySet()
0458: .iterator(); constIter.hasNext();) {
0459: ConstructorHolder ch = (ConstructorHolder) constIter.next();
0460: ConstructorInvocation calledC = ch.getCalledConstructor();
0461: if (calledC == null || ch.isDangerous()) {
0462: continue;
0463: }
0464: //if its not dangerous then evaluate if it should be
0465: //if it calls dangerous constructor mark it as dangerous
0466: int cCount = calledC.getArgumentCount();
0467: for (Iterator innerConstIter = classConstructorMap.keySet()
0468: .iterator(); innerConstIter.hasNext()
0469: && !ch.isDangerous();) {
0470: //forget skipping self because that introduces another check for each, but only 1 hit
0471: ConstructorHolder h2 = (ConstructorHolder) innerConstIter
0472: .next();
0473: if (h2.isDangerous()) {
0474: int matchConstArgCount = h2
0475: .getASTConstructorDeclaration()
0476: .getParameterCount();
0477: if (matchConstArgCount == cCount) {
0478: ch.setDangerous(true);
0479: found = true;
0480: //System.out.println("evaluateDangerOfConstructors2 setting dangerous constructor with " + ch.getASTConstructorDeclaration().getParameterCount() + " params");
0481: }
0482: }
0483: }
0484: }
0485: return found;
0486: }
0487:
0488: /**
0489: * ASTPrimaryPrefix has name in child node of ASTName
0490: *
0491: *@param node Description of Parameter
0492: *@return The NameFromPrefix value
0493: */
0494: private static String getNameFromPrefix(ASTPrimaryPrefix node) {
0495: String name = "";
0496: //should only be 1 child, if more I need more knowledge
0497: if (node.jjtGetNumChildren() == 1) {
0498: //safety check
0499: Node nnode = node.jjtGetFirstChild();
0500: if (nnode instanceof ASTName) {
0501: //just as easy as null check and it should be an ASTName anyway
0502: name = ((ASTName) nnode).getImage();
0503: } else if (nnode instanceof ASTExpression) {
0504: //FIXME? not sure this is correct
0505: List expressions = new ArrayList();
0506: node.findChildrenOfType(ASTName.class, expressions);
0507: if (expressions.size() > 0) {
0508: name = ((ASTName) expressions.get(0)).getImage();
0509: }
0510: }
0511: }
0512: return name;
0513: }
0514:
0515: /**
0516: * ASTPrimarySuffix has name in itself
0517: *
0518: *@param node Description of Parameter
0519: *@return The NameFromSuffix value
0520: */
0521: private static String getNameFromSuffix(ASTPrimarySuffix node) {
0522: return node.getImage();
0523: }
0524:
0525: ////////////////////////////////////////////////////////////////////////////////
0526: ////////////////////////////////////////////////////////////////////////////////
0527: ////////////////////////////////////////////////////////////////////////////////
0528: //Helper methods to process visits
0529:
0530: /**
0531: * Adds a feature to the CalledMethodsOfNode attribute of the ConstructorCallsOverridableMethodRule class
0532: *
0533: *@param node The feature to be added to the CalledMethodsOfNode attribute
0534: *@param calledMethods The feature to be added to the CalledMethodsOfNode attribute
0535: *@param className The feature to be added to the CalledMethodsOfNode attribute
0536: */
0537: private static void addCalledMethodsOfNode(AccessNode node,
0538: List calledMethods, String className) {
0539: List expressions = new ArrayList();
0540: node.findChildrenOfType(ASTPrimaryExpression.class,
0541: expressions, false);
0542: addCalledMethodsOfNodeImpl(expressions, calledMethods,
0543: className);
0544: }
0545:
0546: /**
0547: * Adds all methods called on this instance from within this Node.
0548: *
0549: *@param node The feature to be added to the CalledMethodsOfNode attribute
0550: *@param calledMethods The feature to be added to the CalledMethodsOfNode attribute
0551: *@param className The feature to be added to the CalledMethodsOfNode attribute
0552: */
0553: private static void addCalledMethodsOfNode(SimpleNode node,
0554: List calledMethods, String className) {
0555: List expressions = new ArrayList();
0556: node
0557: .findChildrenOfType(ASTPrimaryExpression.class,
0558: expressions);
0559: addCalledMethodsOfNodeImpl(expressions, calledMethods,
0560: className);
0561: }
0562:
0563: /**
0564: * Adds a feature to the CalledMethodsOfNodeImpl attribute of the ConstructorCallsOverridableMethodRule class
0565: *
0566: *@param expressions The feature to be added to the CalledMethodsOfNodeImpl attribute
0567: *@param calledMethods The feature to be added to the CalledMethodsOfNodeImpl attribute
0568: *@param className The feature to be added to the CalledMethodsOfNodeImpl attribute
0569: */
0570: private static void addCalledMethodsOfNodeImpl(List expressions,
0571: List calledMethods, String className) {
0572: for (Iterator it = expressions.iterator(); it.hasNext();) {
0573: ASTPrimaryExpression ape = (ASTPrimaryExpression) it.next();
0574: MethodInvocation meth = findMethod(ape, className);
0575: if (meth != null) {
0576: //System.out.println("Adding call " + methName);
0577: calledMethods.add(meth);
0578: }
0579: }
0580: }
0581:
0582: /**
0583: *@param node Description of Parameter
0584: *@param className Description of Parameter
0585: *@return A method call on the class passed in, or null if no method call is found.
0586: *@todo Need a better way to match the class and package name to the actual method being called.
0587: */
0588: private static MethodInvocation findMethod(
0589: ASTPrimaryExpression node, String className) {
0590: if (node.jjtGetNumChildren() > 0
0591: && node.jjtGetFirstChild().jjtGetNumChildren() > 0
0592: && node.jjtGetFirstChild().jjtGetFirstChild() instanceof ASTLiteral) {
0593: return null;
0594: }
0595: MethodInvocation meth = MethodInvocation.getMethod(node);
0596: boolean found = false;
0597: // if(meth != null){
0598: // meth.show();
0599: // }
0600: if (meth != null) {
0601: //if it's a call on a variable, or on its superclass ignore it.
0602: if ((meth.getReferenceNames().size() == 0)
0603: && !meth.isSuper()) {
0604: //if this list does not contain our class name, then its not referencing our class
0605: //this is a cheezy test... but it errs on the side of less false hits.
0606: List packClass = meth.getQualifierNames();
0607: if (packClass.size() > 0) {
0608: for (Iterator it = packClass.iterator(); it
0609: .hasNext();) {
0610: String name = (String) it.next();
0611: if (name.equals(className)) {
0612: found = true;
0613: break;
0614: }
0615: }
0616: } else {
0617: found = true;
0618: }
0619: }
0620: }
0621: if (found) {
0622: return meth;
0623: } else {
0624: return null;
0625: }
0626: }
0627:
0628: /**
0629: * Description of the Class
0630: *
0631: *@author Mike Atkinson
0632: */
0633: private final class ConstructorInvocation {
0634: private ASTExplicitConstructorInvocation m_Eci;
0635: private String name;
0636: private int count = 0;
0637:
0638: /**
0639: * Constructor for the ConstructorInvocation object
0640: *
0641: *@param eci Description of Parameter
0642: */
0643: public ConstructorInvocation(
0644: ASTExplicitConstructorInvocation eci) {
0645: m_Eci = eci;
0646: List l = new ArrayList();
0647: eci.findChildrenOfType(ASTArguments.class, l);
0648: if (l.size() > 0) {
0649: ASTArguments aa = (ASTArguments) l.get(0);
0650: count = aa.getArgumentCount();
0651: }
0652: name = eci.getImage();
0653: }
0654:
0655: /**
0656: * Gets the ASTExplicitConstructorInvocation attribute of the ConstructorInvocation object
0657: *
0658: *@return The ASTExplicitConstructorInvocation value
0659: */
0660: public ASTExplicitConstructorInvocation getASTExplicitConstructorInvocation() {
0661: return m_Eci;
0662: }
0663:
0664: /**
0665: * Gets the ArgumentCount attribute of the ConstructorInvocation object
0666: *
0667: *@return The ArgumentCount value
0668: */
0669: public int getArgumentCount() {
0670: return count;
0671: }
0672:
0673: /**
0674: * Gets the Name attribute of the ConstructorInvocation object
0675: *
0676: *@return The Name value
0677: */
0678: public String getName() {
0679: return name;
0680: }
0681: }
0682:
0683: /**
0684: * Description of the Class
0685: *
0686: *@author Mike Atkinson
0687: */
0688: private final class MethodHolder {
0689: private ASTMethodDeclarator m_Amd;
0690: private boolean m_Dangerous = false;
0691:
0692: /**
0693: * Constructor for the MethodHolder object
0694: *
0695: *@param amd Description of Parameter
0696: */
0697: public MethodHolder(ASTMethodDeclarator amd) {
0698: m_Amd = amd;
0699: }
0700:
0701: /**
0702: * Sets the Dangerous attribute of the MethodHolder object
0703: *
0704: *@param dangerous The new Dangerous value
0705: */
0706: public void setDangerous(boolean dangerous) {
0707: m_Dangerous = dangerous;
0708: }
0709:
0710: /**
0711: * Gets the ASTMethodDeclarator attribute of the MethodHolder object
0712: *
0713: *@return The ASTMethodDeclarator value
0714: */
0715: public ASTMethodDeclarator getASTMethodDeclarator() {
0716: return m_Amd;
0717: }
0718:
0719: /**
0720: * Gets the Dangerous attribute of the MethodHolder object
0721: *
0722: *@return The Dangerous value
0723: */
0724: public boolean isDangerous() {
0725: return m_Dangerous;
0726: }
0727: }
0728:
0729: /**
0730: * Description of the Class
0731: *
0732: *@author Mike Atkinson
0733: */
0734: private final class ConstructorHolder {
0735: private ASTConstructorDeclaration m_Cd;
0736: private boolean m_Dangerous = false;
0737: private ConstructorInvocation m_Ci;
0738: private boolean m_CiInitialized = false;
0739:
0740: /**
0741: * Constructor for the ConstructorHolder object
0742: *
0743: *@param cd Description of Parameter
0744: */
0745: public ConstructorHolder(ASTConstructorDeclaration cd) {
0746: m_Cd = cd;
0747: }
0748:
0749: /**
0750: * Sets the Dangerous attribute of the ConstructorHolder object
0751: *
0752: *@param dangerous The new Dangerous value
0753: */
0754: public void setDangerous(boolean dangerous) {
0755: m_Dangerous = dangerous;
0756: }
0757:
0758: /**
0759: * Gets the ASTConstructorDeclaration attribute of the ConstructorHolder object
0760: *
0761: *@return The ASTConstructorDeclaration value
0762: */
0763: public ASTConstructorDeclaration getASTConstructorDeclaration() {
0764: return m_Cd;
0765: }
0766:
0767: /**
0768: * Gets the CalledConstructor attribute of the ConstructorHolder object
0769: *
0770: *@return The CalledConstructor value
0771: */
0772: public ConstructorInvocation getCalledConstructor() {
0773: if (m_CiInitialized == false) {
0774: initCI();
0775: }
0776: return m_Ci;
0777: }
0778:
0779: /**
0780: * Gets the ASTExplicitConstructorInvocation attribute of the ConstructorHolder object
0781: *
0782: *@return The ASTExplicitConstructorInvocation value
0783: */
0784: public ASTExplicitConstructorInvocation getASTExplicitConstructorInvocation() {
0785: ASTExplicitConstructorInvocation eci = null;
0786: if (m_CiInitialized == false) {
0787: initCI();
0788: }
0789: if (m_Ci != null) {
0790: eci = m_Ci.getASTExplicitConstructorInvocation();
0791: }
0792: return eci;
0793: }
0794:
0795: /**
0796: * Gets the Dangerous attribute of the ConstructorHolder object
0797: *
0798: *@return The Dangerous value
0799: */
0800: public boolean isDangerous() {
0801: return m_Dangerous;
0802: }
0803:
0804: /** Description of the Method */
0805: private void initCI() {
0806: List expressions = new ArrayList();
0807: m_Cd
0808: .findChildrenOfType(
0809: ASTExplicitConstructorInvocation.class,
0810: expressions);
0811: //only 1...
0812: if (expressions.size() > 0) {
0813: ASTExplicitConstructorInvocation eci = (ASTExplicitConstructorInvocation) expressions
0814: .get(0);
0815: m_Ci = new ConstructorInvocation(eci);
0816: //System.out.println("Const call " + eci.getImage()); //super or this???
0817: }
0818: m_CiInitialized = true;
0819: }
0820: }
0821:
0822: /**
0823: * 2: method(); ASTPrimaryPrefix ASTName image = "method" ASTPrimarySuffix *ASTArguments 3: a.method();
0824: * ASTPrimaryPrefix -> ASTName image = "a.method" ??? ASTPrimarySuffix -> () ASTArguments 3: this.method();
0825: * ASTPrimaryPrefix -> this image=null //MRA image=="this" ASTPrimarySuffix -> method ASTPrimarySuffix -> ()
0826: * ASTArguments super.method(); ASTPrimaryPrefix -> image = "method" //MRA ASTPrimaryPrefix -> image =
0827: * "super.method" ASTPrimarySuffix -> image = null ASTArguments -> super.a.method(); ASTPrimaryPrefix -> image = "a"
0828: * //MRA ASTPrimaryPrefix -> image = "super.a" ASTPrimarySuffix -> image = "method" ASTPrimarySuffix -> image = null
0829: * ASTArguments -> 4: this.a.method(); ASTPrimaryPrefix -> image = null ASTPrimarySuffix -> image = "a"
0830: * ASTPrimarySuffix -> image = "method" ASTPrimarySuffix -> ASTArguments 4: ClassName.this.method();
0831: * ASTPrimaryPrefix ASTName image = "ClassName" ASTPrimarySuffix -> this image=null ASTPrimarySuffix -> image =
0832: * "method" ASTPrimarySuffix -> () ASTArguments 5: ClassName.this.a.method(); ASTPrimaryPrefix ASTName image =
0833: * "ClassName" ASTPrimarySuffix -> this image=null ASTPrimarySuffix -> image="a" ASTPrimarySuffix -> image="method"
0834: * ASTPrimarySuffix -> () ASTArguments 5: Package.ClassName.this.method(); ASTPrimaryPrefix ASTName image
0835: * ="Package.ClassName" ASTPrimarySuffix -> this image=null ASTPrimarySuffix -> image="method" ASTPrimarySuffix ->
0836: * () ASTArguments 6: Package.ClassName.this.a.method(); ASTPrimaryPrefix ASTName image ="Package.ClassName"
0837: * ASTPrimarySuffix -> this image=null ASTPrimarySuffix -> a ASTPrimarySuffix -> method ASTPrimarySuffix -> ()
0838: * ASTArguments 5: OuterClass.InnerClass.this.method(); ASTPrimaryPrefix ASTName image = "OuterClass.InnerClass"
0839: * ASTPrimarySuffix -> this image=null ASTPrimarySuffix -> method ASTPrimarySuffix -> () ASTArguments 6:
0840: * OuterClass.InnerClass.this.a.method(); ASTPrimaryPrefix ASTName image = "OuterClass.InnerClass" ASTPrimarySuffix
0841: * -> this image=null ASTPrimarySuffix -> a ASTPrimarySuffix -> method ASTPrimarySuffix -> () ASTArguments
0842: * OuterClass.InnerClass.this.a.method().method().method(); ASTPrimaryPrefix ASTName image = "OuterClass.InnerClass"
0843: * ASTPrimarySuffix -> this image=null ASTPrimarySuffix -> a image='a' ASTPrimarySuffix -> method image='method'
0844: * ASTPrimarySuffix -> () image=null ASTArguments ASTPrimarySuffix -> method image='method' ASTPrimarySuffix -> ()
0845: * image=null ASTArguments ASTPrimarySuffix -> method image='method' ASTPrimarySuffix -> () image=null ASTArguments
0846: * 3..n: Class.InnerClass[0].InnerClass[n].this.method(); ASTPrimaryPrefix ASTName image = "Class[0]..InnerClass[n]"
0847: * ASTPrimarySuffix -> image=null ASTPrimarySuffix -> method ASTPrimarySuffix -> () ASTArguments super.aMethod();
0848: * ASTPrimaryPrefix -> aMethod ASTPrimarySuffix -> () Evaluate right to left
0849: *
0850: *@author Mike Atkinson
0851: */
0852: private static class MethodInvocation {
0853: private String m_Name;
0854: private ASTPrimaryExpression m_Ape;
0855: private List m_ReferenceNames;
0856: private List m_QualifierNames;
0857: private int m_ArgumentSize;
0858: private boolean m_Super;
0859:
0860: /**
0861: * Constructor for the MethodInvocation object
0862: *
0863: *@param ape Description of Parameter
0864: *@param qualifierNames Description of Parameter
0865: *@param referenceNames Description of Parameter
0866: *@param name Description of Parameter
0867: *@param argumentSize Description of Parameter
0868: *@param superCall Description of Parameter
0869: */
0870: private MethodInvocation(ASTPrimaryExpression ape,
0871: List qualifierNames, List referenceNames, String name,
0872: int argumentSize, boolean super Call) {
0873: m_Ape = ape;
0874: m_QualifierNames = qualifierNames;
0875: m_ReferenceNames = referenceNames;
0876: m_Name = name;
0877: m_ArgumentSize = argumentSize;
0878: m_Super = super Call;
0879: }
0880:
0881: /**
0882: * Gets the Super attribute of the MethodInvocation object
0883: *
0884: *@return The Super value
0885: */
0886: public boolean isSuper() {
0887: return m_Super;
0888: }
0889:
0890: /**
0891: * Gets the Name attribute of the MethodInvocation object
0892: *
0893: *@return The Name value
0894: */
0895: public String getName() {
0896: return m_Name;
0897: }
0898:
0899: /**
0900: * Gets the ArgumentCount attribute of the MethodInvocation object
0901: *
0902: *@return The ArgumentCount value
0903: */
0904: public int getArgumentCount() {
0905: return m_ArgumentSize;
0906: }
0907:
0908: /**
0909: * Gets the ReferenceNames attribute of the MethodInvocation object
0910: *
0911: *@return The ReferenceNames value
0912: */
0913: public List getReferenceNames() {
0914: return m_ReferenceNames;
0915: //new ArrayList(variableNames);
0916: }
0917:
0918: /**
0919: * Gets the QualifierNames attribute of the MethodInvocation object
0920: *
0921: *@return The QualifierNames value
0922: */
0923: public List getQualifierNames() {
0924: return m_QualifierNames;
0925: }
0926:
0927: /**
0928: * Gets the ASTPrimaryExpression attribute of the MethodInvocation object
0929: *
0930: *@return The ASTPrimaryExpression value
0931: */
0932: public ASTPrimaryExpression getASTPrimaryExpression() {
0933: return m_Ape;
0934: }
0935:
0936: /** Description of the Method */
0937: public void show() {
0938: System.out.println("<MethodInvocation>");
0939: List pkg = getQualifierNames();
0940: System.out.println(" <Qualifiers>");
0941: for (Iterator it = pkg.iterator(); it.hasNext();) {
0942: String name = (String) it.next();
0943: System.out.println(" " + name);
0944: }
0945: System.out.println(" </Qualifiers>");
0946: System.out.println(" <Super>" + isSuper() + "</Super>");
0947: List vars = getReferenceNames();
0948: System.out.println(" <References>");
0949: for (Iterator it = vars.iterator(); it.hasNext();) {
0950: String name = (String) it.next();
0951: System.out.println(" " + name);
0952: }
0953: System.out.println(" </References>");
0954: System.out.println(" <Name>" + getName() + "</Name>");
0955: System.out.println("</MethodInvocation>");
0956: }
0957:
0958: /**
0959: * Gets the Method attribute of the MethodInvocation class
0960: *
0961: *@param node Description of Parameter
0962: *@return The Method value
0963: */
0964: public static MethodInvocation getMethod(
0965: ASTPrimaryExpression node) {
0966: MethodInvocation meth = null;
0967: int i = node.jjtGetNumChildren();
0968: if (i > 1) {
0969: //should always be at least 2, probably can eliminate this check
0970: //start at end which is guaranteed, work backwards
0971: Node lastNode = node.jjtGetChild(i - 1);
0972: if ((lastNode.jjtGetNumChildren() == 1)
0973: && (lastNode.jjtGetFirstChild() instanceof ASTArguments)) {
0974: //could be ASTExpression for instance 'a[4] = 5';
0975: //start putting method together
0976: // System.out.println("Putting method together now");
0977: List varNames = new ArrayList();
0978: List packagesAndClasses = new ArrayList();
0979: //look in JLS for better name here;
0980: String methodName = null;
0981: ASTArguments args = (ASTArguments) lastNode
0982: .jjtGetFirstChild();
0983: int numOfArguments = args.getArgumentCount();
0984: boolean super First = false;
0985: int this Index = -1;
0986:
0987: FIND_SUPER_OR_THIS: {
0988: //search all nodes except last for 'this' or 'super'. will be at: (node 0 | node 1 | nowhere)
0989: //this is an ASTPrimarySuffix with a null image and does not have child (which will be of type ASTArguments)
0990: //this is an ASTPrimaryPrefix with a null image and an ASTName that has a null image
0991: //super is an ASTPrimarySuffix with a null image and does not have child (which will be of type ASTArguments)
0992: //super is an ASTPrimaryPrefix with a non-null image
0993: for (int x = 0; x < i - 1; x++) {
0994: Node child = node.jjtGetChild(x);
0995: if (child instanceof ASTPrimarySuffix) {
0996: //check suffix type match
0997: ASTPrimarySuffix child2 = (ASTPrimarySuffix) child;
0998: // String name = getNameFromSuffix((ASTPrimarySuffix)child);
0999: // System.out.println("found name suffix of : " + name);
1000: //MRA if (child2.getImage() == null && child2.jjtGetNumChildren() == 0) {
1001: if ("this".equals(child2.getImage())) {
1002: this Index = x;
1003: break;
1004: }
1005: //could be super, could be this. currently we cant tell difference so we miss super when
1006: //XYZ.ClassName.super.method();
1007: //still works though.
1008: } else if (child instanceof ASTPrimaryPrefix) {
1009: //check prefix type match
1010: ASTPrimaryPrefix child2 = (ASTPrimaryPrefix) child;
1011: //MRA if (getNameFromPrefix(child2) == null) {
1012: //MRA if (child2.getImage() == null) {
1013: //MRA thisIndex = x;
1014: //MRA break;
1015: //MRA } else {//happens when super is used [super.method(): image = 'method']
1016: //MRA superFirst = true;
1017: //MRA thisIndex = x;
1018: //MRA //the true super is at an unusable index because super.method() has only 2 nodes [method=0,()=1]
1019: //MRA //as opposed to the 3 you might expect and which this.method() actually has. [this=0,method=1.()=2]
1020: //MRA break;
1021: //MRA }
1022: //MRA }
1023: if ("this".equals(child2)) {
1024: this Index = x;
1025: break;
1026: } else if (child2.getImage() != null
1027: && child2.getImage()
1028: .startsWith("super.")) {
1029: super First = true;
1030: this Index = x;
1031: break;
1032: //the true super is at an unusable index because super.method() has only 2 nodes [method=0,()=1]
1033: //as opposed to the 3 you might expect and which this.method() actually has. [this=0,method=1.()=2]
1034: }
1035: }
1036: // else{
1037: // System.err.println("Bad Format error"); //throw exception, quit evaluating this compilation node
1038: // }
1039: }
1040: }
1041:
1042: if (this Index != -1) {
1043: // System.out.println("Found this or super: " + thisIndex);
1044: //Hack that must be removed if and when the patters of super.method() begins to logically match the rest of the patterns !!!
1045: if (super First) {
1046: //this is when super is the first node of statement. no qualifiers, all variables or method
1047: // System.out.println("super first");
1048: FIRSTNODE: {
1049: ASTPrimaryPrefix child = (ASTPrimaryPrefix) node
1050: .jjtGetFirstChild();
1051: String name = child.getImage();
1052: //special case
1053: if (i == 2) {
1054: //last named node = method name
1055: methodName = name;
1056: } else {
1057: //not the last named node so its only var name
1058: varNames.add(name);
1059: }
1060: }
1061: OTHERNODES: {
1062: //variables
1063: for (int x = 1; x < i - 1; x++) {
1064: Node child = node.jjtGetChild(x);
1065: ASTPrimarySuffix ps = (ASTPrimarySuffix) child;
1066: if (ps.isArguments() == false) {
1067: String name = ((ASTPrimarySuffix) child)
1068: .getImage();
1069: if (x == i - 2) {
1070: //last node
1071: methodName = name;
1072: } else {
1073: //not the last named node so its only var name
1074: varNames.add(name);
1075: }
1076: }
1077: }
1078: }
1079: } else {
1080: //not super call
1081: FIRSTNODE: {
1082: if (this Index == 1) {
1083: //qualifiers in node 0
1084: ASTPrimaryPrefix child = (ASTPrimaryPrefix) node
1085: .jjtGetFirstChild();
1086: String toParse = getNameFromPrefix(child);
1087: // System.out.println("parsing for class/package names in : " + toParse);
1088: java.util.StringTokenizer st = new java.util.StringTokenizer(
1089: toParse, ".");
1090: while (st.hasMoreTokens()) {
1091: packagesAndClasses.add(st
1092: .nextToken());
1093: }
1094: }
1095: }
1096: OTHERNODES: {
1097: //other methods called in this statement are grabbed here
1098: //this is at 0, then no Qualifiers
1099: //this is at 1, the node 0 contains qualifiers
1100: for (int x = this Index + 1; x < i - 1; x++) {
1101: //everything after this is var name or method name
1102: ASTPrimarySuffix child = (ASTPrimarySuffix) node
1103: .jjtGetChild(x);
1104: if (child.isArguments() == false) {
1105: //skip the () of method calls
1106: String name = getNameFromSuffix(child);
1107: // System.out.println("Found suffix: " + name);
1108: if (x == i - 2) {
1109: methodName = name;
1110: } else {
1111: varNames.add(name);
1112: }
1113: }
1114: }
1115: }
1116: }
1117: } else {
1118: //if no this or super found, everything is method name or variable
1119: //System.out.println("no this found:");
1120: FIRSTNODE: {
1121: //variable names are in the prefix + the first method call [a.b.c.x()]
1122: ASTPrimaryPrefix child = (ASTPrimaryPrefix) node
1123: .jjtGetFirstChild();
1124: String toParse = getNameFromPrefix(child);
1125: // System.out.println("parsing for var names in : " + toParse);
1126: java.util.StringTokenizer st = new java.util.StringTokenizer(
1127: toParse, ".");
1128: while (st.hasMoreTokens()) {
1129: String value = st.nextToken();
1130: if (!st.hasMoreTokens()) {
1131: if (i == 2) {
1132: //if this expression is 2 nodes long, then the last part of prefix is method name
1133: methodName = value;
1134: } else {
1135: varNames.add(value);
1136: }
1137: } else {
1138: //variable name
1139: varNames.add(value);
1140: }
1141: }
1142: }
1143: OTHERNODES: {
1144: //other methods called in this statement are grabbed here
1145: for (int x = 1; x < i - 1; x++) {
1146: ASTPrimarySuffix child = (ASTPrimarySuffix) node
1147: .jjtGetChild(x);
1148: if (child.isArguments() == false) {
1149: String name = getNameFromSuffix(child);
1150: if (x == i - 2) {
1151: methodName = name;
1152: } else {
1153: varNames.add(name);
1154: }
1155: }
1156: }
1157: }
1158: }
1159: meth = new MethodInvocation(node,
1160: packagesAndClasses, varNames, methodName,
1161: numOfArguments, super First);
1162: }
1163: }
1164: return meth;
1165: }
1166: }
1167:
1168: /**
1169: * 1 package per class. holds info for evaluating a single class.
1170: *
1171: *@author Mike Atkinson
1172: */
1173: private static class EvalPackage {
1174:
1175: /** Description of the Field */
1176: public String m_ClassName;
1177: /** Description of the Field */
1178: public List calledMethods;
1179: /** Description of the Field */
1180: public Map allMethodsOfClass;
1181:
1182: /** Description of the Field */
1183: public List calledConstructors;
1184: /** Description of the Field */
1185: public Map allPrivateConstructorsOfClass;
1186:
1187: /** Constructor for the EvalPackage object */
1188: public EvalPackage() {
1189: }
1190:
1191: /**
1192: * Constructor for the EvalPackage object
1193: *
1194: *@param className Description of Parameter
1195: */
1196: public EvalPackage(String className) {
1197: m_ClassName = className;
1198: calledMethods = new ArrayList();
1199: //meths called from constructor
1200: allMethodsOfClass = new HashMap();
1201: calledConstructors = new ArrayList();
1202: //all constructors called from constructor
1203: allPrivateConstructorsOfClass = new HashMap();
1204: }
1205: }
1206:
1207: /**
1208: * Description of the Class
1209: *
1210: *@author Mike Atkinson
1211: */
1212: private final static class NullEvalPackage extends EvalPackage {
1213: /** Constructor for the NullEvalPackage object */
1214: public NullEvalPackage() {
1215: m_ClassName = "";
1216: calledMethods = Collections.EMPTY_LIST;
1217: allMethodsOfClass = Collections.EMPTY_MAP;
1218: calledConstructors = Collections.EMPTY_LIST;
1219: allPrivateConstructorsOfClass = Collections.EMPTY_MAP;
1220: }
1221: }
1222: }
|