View Javadoc

    1   /*
    2    * Created on 20.07.2004
    3    */
    4   package net.sourceforge.pmd.dfa;
    5
    6   import net.sourceforge.pmd.AbstractRule;
    7   import net.sourceforge.pmd.PropertyDescriptor;
    8   import net.sourceforge.pmd.RuleContext;
    9   import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
    10  import net.sourceforge.pmd.ast.ASTMethodDeclaration;
    11  import net.sourceforge.pmd.ast.SimpleNode;
    12  import net.sourceforge.pmd.dfa.pathfinder.CurrentPath;
    13  import net.sourceforge.pmd.dfa.pathfinder.DAAPathFinder;
    14  import net.sourceforge.pmd.dfa.pathfinder.Executable;
    15  import net.sourceforge.pmd.dfa.variableaccess.VariableAccess;
    16  import net.sourceforge.pmd.properties.IntegerProperty;
    17
    18  import java.text.MessageFormat;
    19  import java.util.ArrayList;
    20  import java.util.Hashtable;
    21  import java.util.Iterator;
    22  import java.util.List;
    23  import java.util.Map;
    24
    25  /***
    26   * Starts path search for each method and runs code if found.
    27   *
    28   * @author raik
    29   * @author Sven Jacob
    30   */
    31  public class DaaRule extends AbstractRule implements Executable {
    32      private RuleContext rc;
    33      private List daaRuleViolations;
    34      private int maxRuleViolations;
    35      private int currentRuleViolationCount;
    36
    37      private static final PropertyDescriptor maxPathDescriptor = new IntegerProperty(
    38              "maxpaths", "Maximum number of paths per method", 5000, 1.0f
    39              );
    40
    41      private static final PropertyDescriptor maxViolationsDescriptor = new IntegerProperty(
    42              "maxviolations", "Maximum number of anomalys per class", 1000, 2.0f
    43              );
    44
    45      private static final Map propertyDescriptorsByName = asFixedMap(
    46              new PropertyDescriptor[] { maxPathDescriptor, maxViolationsDescriptor});
    47
    48      protected Map propertiesByName() {
    49          return propertyDescriptorsByName;
    50      }
    51
    52      private static class Usage {
    53          public int accessType;
    54          public IDataFlowNode node;
    55
    56          public Usage(int accessType, IDataFlowNode node) {
    57              this.accessType = accessType;
    58              this.node = node;
    59          }
    60
    61          public String toString() {
    62              return "accessType = " + accessType + ", line = " + node.getLine();
    63          }
    64      }
    65
    66      public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
    67          this.maxRuleViolations = getIntProperty(maxViolationsDescriptor);
    68          this.currentRuleViolationCount = 0;
    69          return super.visit(node, data);
    70      }
    71
    72      public Object visit(ASTMethodDeclaration methodDeclaration, Object data) {
    73          this.rc = (RuleContext) data;
    74          this.daaRuleViolations = new ArrayList();
    75
    76          final IDataFlowNode node = (IDataFlowNode) methodDeclaration.getDataFlowNode().getFlow().get(0);
    77
    78          final DAAPathFinder pathFinder = new DAAPathFinder(node, this, getIntProperty(maxPathDescriptor));
    79          pathFinder.run();
    80
    81          super.visit(methodDeclaration, data);
    82          return data;
    83      }
    84
    85      public void execute(CurrentPath path) {
    86          if (maxNumberOfViolationsReached()) {
    87              // dont execute this path if the limit is already reached
    88              return;
    89          }
    90
    91          final Hashtable hash = new Hashtable();
    92
    93          final Iterator pathIterator = path.iterator();
    94          while (pathIterator.hasNext()) {
    95              // iterate all nodes in this path
    96              IDataFlowNode inode = (IDataFlowNode) pathIterator.next();
    97              if (inode.getVariableAccess() != null) {
    98                  // iterate all variables of this node
    99                  for (int g = 0; g < inode.getVariableAccess().size(); g++) {
    100                     final VariableAccess va = (VariableAccess) inode.getVariableAccess().get(g);
    101
    102                     // get the last usage of the current variable
    103                     final Usage lastUsage = (Usage) hash.get(va.getVariableName());
    104                     if (lastUsage != null) {
    105                         // there was a usage to this variable before
    106                         checkVariableAccess(inode, va, lastUsage);
    107                     }
    108
    109                     final Usage newUsage = new Usage(va.getAccessType(), inode);
    110                     // put the new usage for the variable
    111                     hash.put(va.getVariableName(), newUsage);
    112                 }
    113             }
    114         }
    115     }
    116
    117     /***
    118      * @param inode
    119      * @param va
    120      * @param o
    121      */
    122     private void checkVariableAccess(IDataFlowNode inode, VariableAccess va, final Usage u) {
    123         // get the start and end line
    124         final int startLine = u.node.getLine();
    125         final int endLine = inode.getLine();
    126
    127         final SimpleNode lastNode = inode.getSimpleNode();
    128         final SimpleNode firstNode = u.node.getSimpleNode();
    129
    130         if (va.accessTypeMatches(u.accessType) && va.isDefinition() ) { // DD
    131             addDaaViolation(rc, lastNode, "DD", va.getVariableName(), startLine, endLine);
    132         } else if (u.accessType == VariableAccess.UNDEFINITION && va.isReference()) { // UR
    133             addDaaViolation(rc, lastNode, "UR", va.getVariableName(), startLine, endLine);
    134         } else if (u.accessType == VariableAccess.DEFINITION && va.isUndefinition()) { // DU
    135             addDaaViolation(rc, firstNode, "DU", va.getVariableName(), startLine, endLine);
    136         }
    137     }
    138
    139     /***
    140      * Adds a daa violation to the report.
    141      *
    142      * @param ctx  the RuleContext
    143      * @param node the node that produces the violation
    144      * @param msg  specific message to put in the report
    145      */
    146     private final void addDaaViolation(Object data, SimpleNode node, String type, String var, int startLine, int endLine) {
    147         if (!maxNumberOfViolationsReached()
    148                 && !violationAlreadyExists(type, var, startLine, endLine)
    149                 && node != null) {
    150             final RuleContext ctx = (RuleContext) data;
    151             final Object[] params = new Object[] { type, var, new Integer(startLine), new Integer(endLine) };
    152             String msg = type;
    153             if (getMessage() != null) {
    154                 msg = MessageFormat.format(getMessage(), params);
    155             }
    156             final DaaRuleViolation violation = new DaaRuleViolation(this, ctx, node, type, msg, var, startLine, endLine);
    157             ctx.getReport().addRuleViolation(violation);
    158             this.daaRuleViolations.add(violation);
    159             this.currentRuleViolationCount++;
    160       }
    161     }
    162
    163     /***
    164      * Maximum number of violations was already reached?
    165      * @return
    166      */
    167     private boolean maxNumberOfViolationsReached() {
    168         return this.currentRuleViolationCount >= this.maxRuleViolations;
    169     }
    170
    171     /***
    172      * Checks if a violation already exists.
    173      * This is needed because on the different paths same anomalies can occur.
    174      * @param type
    175      * @param var
    176      * @param startLine
    177      * @param endLine
    178      * @return true if the violation already was added to the report
    179      */
    180     private boolean violationAlreadyExists(String type, String var, int startLine, int endLine) {
    181         final Iterator violationIterator = this.daaRuleViolations.iterator();
    182         while (violationIterator.hasNext()) {
    183             final DaaRuleViolation violation = (DaaRuleViolation)violationIterator.next();
    184             if ((violation.getBeginLine() == startLine)
    185                     && (violation.getEndLine() == endLine)
    186                     && violation.getType().equals(type)
    187                     && violation.getVariableName().equals(var)) {
    188                 return true;
    189             }
    190         }
    191         return false;
    192     }
    193 }