View Javadoc

    1   package net.sourceforge.pmd.lang.java.rule.imports;
    2
    3   import java.util.ArrayList;
    4   import java.util.List;
    5
    6   import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
    7   import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
    8   import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration;
    9   import net.sourceforge.pmd.lang.java.ast.ASTName;
    10  import net.sourceforge.pmd.lang.java.ast.ASTPackageDeclaration;
    11  import net.sourceforge.pmd.lang.java.ast.JavaNode;
    12  import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
    13
    14  public class UnnecessaryFullyQualifiedNameRule extends AbstractJavaRule {
    15
    16      private List<ASTImportDeclaration> imports = new ArrayList<ASTImportDeclaration>();
    17      private List<ASTImportDeclaration> matches = new ArrayList<ASTImportDeclaration>();
    18
    19      public UnnecessaryFullyQualifiedNameRule() {
    20  	super.addRuleChainVisit(ASTCompilationUnit.class);
    21  	super.addRuleChainVisit(ASTImportDeclaration.class);
    22  	super.addRuleChainVisit(ASTClassOrInterfaceType.class);
    23  	super.addRuleChainVisit(ASTName.class);
    24      }
    25
    26      @Override
    27      public Object visit(ASTCompilationUnit node, Object data) {
    28  	imports.clear();
    29  	return data;
    30      }
    31
    32      @Override
    33      public Object visit(ASTImportDeclaration node, Object data) {
    34  	imports.add(node);
    35  	return data;
    36      }
    37
    38      @Override
    39      public Object visit(ASTClassOrInterfaceType node, Object data) {
    40  	checkImports(node, data, false);
    41  	return data;
    42      }
    43
    44      @Override
    45      public Object visit(ASTName node, Object data) {
    46  	if (!(node.jjtGetParent() instanceof ASTImportDeclaration)
    47  	        && !(node.jjtGetParent() instanceof ASTPackageDeclaration)) {
    48  	    checkImports(node, data, true);
    49  	}
    50  	return data;
    51      }
    52
    53      private void checkImports(JavaNode node, Object data, boolean checkStatic) {
    54  	String name = node.getImage();
    55  	matches.clear();
    56
    57  	//  Find all "matching" import declarations
    58  	for (ASTImportDeclaration importDeclaration : imports) {
    59  	    if (importDeclaration.isImportOnDemand()) {
    60  		// On demand import exactly matches the package of the type
    61  		if (name.startsWith(importDeclaration.getImportedName())) {
    62  		    if (name.lastIndexOf('.') == importDeclaration.getImportedName().length()) {
    63  			matches.add(importDeclaration);
    64  			continue;
    65  		    }
    66  		}
    67  	    } else {
    68  		// Exact match of imported class
    69  		if (name.equals(importDeclaration.getImportedName())) {
    70  		    matches.add(importDeclaration);
    71  		    continue;
    72  		}
    73  		// Match of static method call on imported class
    74  		if (name.startsWith(importDeclaration.getImportedName())) {
    75  		    if (name.lastIndexOf('.') == importDeclaration.getImportedName().length()) {
    76  			matches.add(importDeclaration);
    77  			continue;
    78  		    }
    79  		}
    80  	    }
    81  	}
    82
    83  	// If there is no direct match, consider if we match the tail end of a
    84  	// direct static import, but also a static method on a class import?
    85  	// For example:
    86  	//
    87  	//    import java.util.Arrays;
    88  	//    import static java.util.Arrays.asList;
    89  	//    static {
    90  	//       List list1 = Arrays.asList("foo");  // Array class name not needed!
    91  	//       List list2 = asList("foo"); // Preferred, used static import
    92  	//    }
    93  	if (matches.isEmpty() && name.indexOf('.') >= 0) {
    94  	    for (ASTImportDeclaration importDeclaration : imports) {
    95  		if (importDeclaration.isStatic()) {
    96  		    String[] importParts = importDeclaration.getImportedName().split("\\.");
    97  		    String[] nameParts = name.split("\\.");
    98  		    boolean checkClassImport = false;
    99  		    if (importDeclaration.isImportOnDemand()) {
    100 			//  Name class part matches class part of static import?
    101 			if (nameParts[nameParts.length - 2].equals(importParts[importParts.length - 1])) {
    102 			    checkClassImport = true;
    103 			}
    104 		    } else {
    105 			// Last 2 parts match?
    106 			if (nameParts[nameParts.length - 1].equals(importParts[importParts.length - 1])
    107 				&& nameParts[nameParts.length - 2].equals(importParts[importParts.length - 2])) {
    108 			    checkClassImport = true;
    109 			}
    110 		    }
    111 		    if (checkClassImport) {
    112 			// Name class part matches a direct class import?
    113 			String nameEnd = "." + nameParts[nameParts.length - 2];
    114 			for (ASTImportDeclaration importDeclaration2 : imports) {
    115 			    if (!importDeclaration2.isStatic() && !importDeclaration2.isImportOnDemand()
    116 				    && importDeclaration2.getImportedName().endsWith(nameEnd)) {
    117 				matches.add(importDeclaration2);
    118 			    }
    119 			}
    120 		    }
    121 		}
    122 	    }
    123 	}
    124
    125 	if (!matches.isEmpty()) {
    126 	    String importStr = matches.get(0).getImportedName() + (matches.get(0).isImportOnDemand() ? ".*" : "");
    127 	    addViolation(data, node, new Object[] { node.getImage(), importStr });
    128 	}
    129
    130 	matches.clear();
    131     }
    132 }