View Javadoc

    1   /**
    2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
    3    */
    4   package net.sourceforge.pmd.rules.imports;
    5
    6   import net.sourceforge.pmd.AbstractRule;
    7   import net.sourceforge.pmd.ast.ASTClassOrInterfaceType;
    8   import net.sourceforge.pmd.ast.ASTCompilationUnit;
    9   import net.sourceforge.pmd.ast.ASTImportDeclaration;
    10  import net.sourceforge.pmd.ast.ASTName;
    11  import net.sourceforge.pmd.ast.Comment;
    12  import net.sourceforge.pmd.ast.FormalComment;
    13  import net.sourceforge.pmd.ast.SimpleJavaNode;
    14  import net.sourceforge.pmd.ast.SimpleNode;
    15  import net.sourceforge.pmd.rules.ImportWrapper;
    16
    17  import java.util.HashSet;
    18  import java.util.Set;
    19  import java.util.regex.Matcher;
    20  import java.util.regex.Pattern;
    21
    22  public class UnusedImportsRule extends AbstractRule {
    23
    24      protected Set<ImportWrapper> imports = new HashSet<ImportWrapper>();
    25
    26      public Object visit(ASTCompilationUnit node, Object data) {
    27          imports.clear();
    28          super.visit(node, data);
    29          visitComments(node);
    30          for (ImportWrapper wrapper : imports) {
    31              addViolation(data, wrapper.getNode(), wrapper.getFullName());
    32          }
    33          return data;
    34      }
    35
    36      /*
    37       * Patterns to match the following constructs:
    38       *
    39       * @see  package.class#member  label
    40       * {@linkplain  package.class#member  label}
    41       * {@link  package.class#member  label}
    42       * {@value  package.class#field}
    43       */
    44      private static final Pattern SEE_PATTERN = Pattern.compile(
    45              "@see\\s+(\\p{Alpha}\\p{Alnum}*)[\\s#]");
    46
    47      private static final Pattern LINK_PATTERNS = Pattern.compile(
    48              "\\{@link(?:plain)?\\s+(\\p{Alpha}\\p{Alnum}*)[\\s#\\}]");
    49
    50      private static final Pattern VALUE_PATTERN = Pattern.compile(
    51              "\\{@value\\s+(\\p{Alpha}\\p{Alnum}*)[\\s#\\}]");
    52
    53      private static final Pattern[] PATTERNS = { SEE_PATTERN, LINK_PATTERNS, VALUE_PATTERN };
    54
    55      private void visitComments(ASTCompilationUnit node) {
    56          if (imports.isEmpty()) {
    57              return;
    58          }
    59          for (Comment comment: node.getComments()) {
    60              if (!(comment instanceof FormalComment)) {
    61                  continue;
    62              }
    63              for (Pattern p: PATTERNS) {
    64                  Matcher m = p.matcher(comment.getImage());
    65                  while (m.find()) {
    66                      String s = m.group(1);
    67                      ImportWrapper candidate = new ImportWrapper(s, s, new SimpleJavaNode(-1));
    68
    69                      if (imports.contains(candidate)) {
    70                          imports.remove(candidate);
    71                          if (imports.isEmpty()) {
    72                              return;
    73                          }
    74                      }
    75                  }
    76              }
    77          }
    78      }
    79
    80      public Object visit(ASTImportDeclaration node, Object data) {
    81          if (!node.isImportOnDemand()) {
    82              ASTName importedType = (ASTName) node.jjtGetChild(0);
    83              String className;
    84              if (isQualifiedName(importedType)) {
    85                  int lastDot = importedType.getImage().lastIndexOf('.') + 1;
    86                  className = importedType.getImage().substring(lastDot);
    87              } else {
    88                  className = importedType.getImage();
    89              }
    90              imports.add(new ImportWrapper(importedType.getImage(), className, node));
    91          }
    92
    93          return data;
    94      }
    95
    96      public Object visit(ASTClassOrInterfaceType node, Object data) {
    97          check(node);
    98          return super.visit(node, data);
    99      }
    100
    101     public Object visit(ASTName node, Object data) {
    102         check(node);
    103         return data;
    104     }
    105
    106     protected void check(SimpleNode node) {
    107         if (imports.isEmpty()) {
    108             return;
    109         }
    110         ImportWrapper candidate = getImportWrapper(node);
    111         if (imports.contains(candidate)) {
    112             imports.remove(candidate);
    113         }
    114     }
    115
    116     protected ImportWrapper getImportWrapper(SimpleNode node) {
    117         String name;
    118         if (!isQualifiedName(node)) {
    119             name = node.getImage();
    120         } else {
    121             name = node.getImage().substring(0, node.getImage().indexOf('.'));
    122         }
    123         ImportWrapper candidate = new ImportWrapper(node.getImage(), name, new SimpleJavaNode(-1));
    124         return candidate;
    125     }
    126 }