/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira.rules
 */
package net.ranides.assira.rules.utils;

import java.util.List;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator;
import net.sourceforge.pmd.lang.java.ast.ASTName;
import net.sourceforge.pmd.lang.java.ast.ASTNameList;
import net.sourceforge.pmd.lang.java.ast.ASTType;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public final class ASTUtils {
    
    // @todo (assira.rules #1) return array[0] should be allowed!
    // napisać własną wersję, która wykrywa tablice odrobinę mądrzej
    
    private ASTUtils() {
        /* utility class */
    }
    
    public static boolean isType(ASTType type, Class<?> clazz) {
        return type.getType() == clazz 
            || clazz.getSimpleName().equals(type.getTypeImage())
            || clazz.getName().equals(type.getTypeImage());
    }
    
    public static boolean isType(ASTName type, Class<?> clazz) {
        return type.getType() == clazz 
            || clazz.getSimpleName().equals(type.getImage())
            || clazz.getName().equals(type.getImage());
    }
    
    public static boolean isTypeClass(Node type) {
        return 
            (type instanceof ASTClassOrInterfaceDeclaration)
            && !((ASTClassOrInterfaceDeclaration) type).isInterface();
    }
    
    public static boolean isMethod(ASTMethodDeclaration method, String name, Class... params) {
        if( !name.equals(method.getMethodName()) ) {
            return false;
        }
        
        List<ASTFormalParameter> iparams = getParams(method);
        if(iparams.size() != params.length) {
            return false;
        }
        for(int i=0; i<params.length; i++) {
            ASTType type = iparams.get(i).getTypeNode();
            if( !ASTUtils.isType(type, params[i]) ) {
                return false;
            }
        }
        return true;
    }
    
    public static boolean throwsOne(ASTMethodDeclaration method, Class<? extends Throwable> exception) {
        ASTNameList throwsList = method.getThrows();
        if (throwsList != null && throwsList.jjtGetNumChildren() == 1) {
            ASTName node = (ASTName)throwsList.jjtGetChild(0);
            return isType(node, exception);
        }
        return false;
    }
    
    public static List<ASTFormalParameter> getParams(ASTMethodDeclaration method) {
        ASTMethodDeclarator declarator = method.getFirstDescendantOfType(ASTMethodDeclarator.class);
        return declarator.findDescendantsOfType(ASTFormalParameter.class);
    }
}
