View Javadoc

    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.PropertyDescriptor;
    8   import net.sourceforge.pmd.ast.ASTBlock;
    9   import net.sourceforge.pmd.ast.ASTClassOrInterfaceType;
    10  import net.sourceforge.pmd.ast.ASTCompilationUnit;
    11  import net.sourceforge.pmd.ast.ASTLocalVariableDeclaration;
    12  import net.sourceforge.pmd.ast.ASTMethodDeclaration;
    13  import net.sourceforge.pmd.ast.ASTName;
    14  import net.sourceforge.pmd.ast.ASTReferenceType;
    15  import net.sourceforge.pmd.ast.ASTTryStatement;
    16  import net.sourceforge.pmd.ast.ASTType;
    17  import net.sourceforge.pmd.ast.ASTVariableDeclaratorId;
    18  import net.sourceforge.pmd.ast.Node;
    19  import net.sourceforge.pmd.properties.StringProperty;
    20
    21  import java.util.ArrayList;
    22  import java.util.HashSet;
    23  import java.util.Iterator;
    24  import java.util.List;
    25  import java.util.Map;
    26  import java.util.Set;
    27  import java.util.StringTokenizer;
    28
    29  /***
    30   * Makes sure you close your database connections. It does this by
    31   * looking for code patterned like this:
    32   * <pre>
    33   *  Connection c = X;
    34   *  try {
    35   *   // do stuff, and maybe catch something
    36   *  } finally {
    37   *   c.close();
    38   *  }
    39   * </pre>
    40   */
    41  public class CloseResource extends AbstractRule {
    42
    43      private Set types = new HashSet();
    44
    45
    46      private Set closeTargets = new HashSet();
    47      private static final PropertyDescriptor closeTargetsDescriptor = new StringProperty("closeTargets",
    48              "Methods which may close this resource", "", 1.0f);
    49
    50      private static final PropertyDescriptor typesDescriptor = new StringProperty("types",
    51              "Types that are affected by this rule", "", 2.0f);
    52
    53      private static final Map propertyDescriptorsByName = asFixedMap(new PropertyDescriptor[] { typesDescriptor, closeTargetsDescriptor });
    54
    55      protected Map propertiesByName() {
    56          return propertyDescriptorsByName;
    57      };
    58
    59      public Object visit(ASTCompilationUnit node, Object data) {
    60          if (closeTargets.isEmpty() && getStringProperty(closeTargetsDescriptor) != null) {
    61              for (StringTokenizer st = new StringTokenizer(getStringProperty(closeTargetsDescriptor), ","); st.hasMoreTokens();) {
    62                  closeTargets.add(st.nextToken());
    63              }
    64          }
    65          if (types.isEmpty() && getStringProperty(typesDescriptor) != null) {
    66              for (StringTokenizer st = new StringTokenizer(getStringProperty(typesDescriptor), ","); st.hasMoreTokens();) {
    67                  types.add(st.nextToken());
    68              }
    69          }
    70          return super.visit(node, data);
    71      }
    72
    73      public Object visit(ASTMethodDeclaration node, Object data) {
    74          List vars = node.findChildrenOfType(ASTLocalVariableDeclaration.class);
    75          List ids = new ArrayList();
    76
    77          // find all variable references to Connection objects
    78          for (Iterator it = vars.iterator(); it.hasNext();) {
    79              ASTLocalVariableDeclaration var = (ASTLocalVariableDeclaration) it.next();
    80              ASTType type = var.getTypeNode();
    81
    82              if (type.jjtGetChild(0) instanceof ASTReferenceType) {
    83                  ASTReferenceType ref = (ASTReferenceType) type.jjtGetChild(0);
    84                  if (ref.jjtGetChild(0) instanceof ASTClassOrInterfaceType) {
    85                      ASTClassOrInterfaceType clazz = (ASTClassOrInterfaceType) ref.jjtGetChild(0);
    86                      if (types.contains(clazz.getImage())) {
    87                          ASTVariableDeclaratorId id = (ASTVariableDeclaratorId) var.jjtGetChild(1).jjtGetChild(0);
    88                          ids.add(id);
    89                      }
    90                  }
    91              }
    92          }
    93
    94          // if there are connections, ensure each is closed.
    95          for (int i = 0; i < ids.size(); i++) {
    96              ASTVariableDeclaratorId x = (ASTVariableDeclaratorId) ids.get(i);
    97              ensureClosed((ASTLocalVariableDeclaration) x.jjtGetParent().jjtGetParent(), x, data);
    98          }
    99          return data;
    100     }
    101
    102     private void ensureClosed(ASTLocalVariableDeclaration var,
    103                               ASTVariableDeclaratorId id, Object data) {
    104         // What are the chances of a Connection being instantiated in a
    105         // for-loop init block? Anyway, I'm lazy!
    106         String target = id.getImage() + ".close";
    107         Node n = var;
    108
    109         while (!((n = n.jjtGetParent()) instanceof ASTBlock)) ;
    110
    111         ASTBlock top = (ASTBlock) n;
    112
    113         List tryblocks = new ArrayList();
    114         top.findChildrenOfType(ASTTryStatement.class, tryblocks, true);
    115
    116         boolean closed = false;
    117
    118         // look for try blocks below the line the variable was
    119         // introduced and make sure there is a .close call in a finally
    120         // block.
    121         for (Iterator it = tryblocks.iterator(); it.hasNext();) {
    122             ASTTryStatement t = (ASTTryStatement) it.next();
    123
    124             if ((t.getBeginLine() > id.getBeginLine()) && (t.hasFinally())) {
    125                 ASTBlock f = (ASTBlock) t.getFinally().jjtGetChild(0);
    126                 List names = new ArrayList();
    127                 f.findChildrenOfType(ASTName.class, names, true);
    128                 for (Iterator it2 = names.iterator(); it2.hasNext();) {
    129                     String name = ((ASTName) it2.next()).getImage();
    130                     if (name.equals(target) || closeTargets.contains(name)) {
    131                         closed = true;
    132                     }
    133                 }
    134             }
    135         }
    136
    137         // if all is not well, complain
    138         if (!closed) {
    139             ASTType type = (ASTType) var.jjtGetChild(0);
    140             ASTReferenceType ref = (ASTReferenceType) type.jjtGetChild(0);
    141             ASTClassOrInterfaceType clazz = (ASTClassOrInterfaceType) ref.jjtGetChild(0);
    142             addViolation(data, id, clazz.getImage());
    143         }
    144     }
    145 }