/**
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
 */
package net.sourceforge.pmd.lang.java.rule.migrating;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.ast.ASTCastExpression;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTName;
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
import net.sourceforge.pmd.lang.symboltable.NameOccurrence;

/**
 * This is a rule, that detects unnecessary casts when using Java 1.5 generics and
 * collections.
 *
 * <p>Example:
 * <pre>
 * List<Double> list = new ArrayList<Double>();
 * ...
 * Double d = (Double) list.get(0); //The cast is unnecessary on this typed array.
 * </pre>
 *
 * @see <a href="http://sourceforge.net/p/pmd/discussion/188192/thread/276fd6f0">Java 5 rules: Unnecessary casts/Iterators</a>
 */
//TODO This is not referenced by any RuleSet?
public class UnnecessaryCastRule extends AbstractJavaRule {

    private static Set<String> implClassNames = new HashSet<String>();

    static {
        implClassNames.add("List");
        implClassNames.add("Set");
        implClassNames.add("Map");
        implClassNames.add("java.util.List");
        implClassNames.add("java.util.Set");
        implClassNames.add("java.util.Map");
        implClassNames.add("ArrayList");
        implClassNames.add("HashSet");
        implClassNames.add("HashMap");
        implClassNames.add("LinkedHashMap");
        implClassNames.add("LinkedHashSet");
        implClassNames.add("TreeSet");
        implClassNames.add("TreeMap");
        implClassNames.add("Vector");
        implClassNames.add("java.util.ArrayList");
        implClassNames.add("java.util.HashSet");
        implClassNames.add("java.util.HashMap");
        implClassNames.add("java.util.LinkedHashMap");
        implClassNames.add("java.util.LinkedHashSet");
        implClassNames.add("java.util.TreeSet");
        implClassNames.add("java.util.TreeMap");
        implClassNames.add("java.util.Vector");
    }

    @Override
    public Object visit(ASTLocalVariableDeclaration node, Object data) {
        return process(node, data);
    }

    @Override
    public Object visit(ASTFieldDeclaration node, Object data) {
        return process(node, data);
    }

    private Object process(Node node, Object data) {
        ASTClassOrInterfaceType cit = node.getFirstDescendantOfType(ASTClassOrInterfaceType.class);
        if (cit == null || !implClassNames.contains(cit.getImage())) {
            return data;
        }
        cit = cit.getFirstDescendantOfType(ASTClassOrInterfaceType.class);
        if (cit == null) {
            return data;
        }
        ASTVariableDeclaratorId decl = node.getFirstDescendantOfType(ASTVariableDeclaratorId.class);
        List<NameOccurrence> usages = decl.getUsages();
        for (NameOccurrence no: usages) {
            ASTName name = (ASTName) no.getLocation();
            Node n = name.jjtGetParent().jjtGetParent().jjtGetParent();
            if (n instanceof ASTCastExpression) {
                addViolation(data, n);
            }
        }
        return null;
    }
}
