/**
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
 */

package net.sourceforge.pmd.lang.java.rule.errorprone;

import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.ast.ASTAssignmentOperator;
import net.sourceforge.pmd.lang.java.ast.ASTExpression;
import net.sourceforge.pmd.lang.java.ast.ASTForStatement;
import net.sourceforge.pmd.lang.java.ast.ASTIfStatement;
import net.sourceforge.pmd.lang.java.ast.ASTPostfixExpression;
import net.sourceforge.pmd.lang.java.ast.ASTPreDecrementExpression;
import net.sourceforge.pmd.lang.java.ast.ASTPreIncrementExpression;
import net.sourceforge.pmd.lang.java.ast.ASTWhileStatement;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
import net.sourceforge.pmd.properties.BooleanProperty;
import net.sourceforge.pmd.properties.PropertySource;

/**
 *
 *
 */
public class AssignmentInOperandRule extends AbstractJavaRule {

    private static final BooleanProperty ALLOW_IF_DESCRIPTOR = new BooleanProperty("allowIf",
            "Allow assignment within the conditional expression of an if statement", false, 1.0f);

    private static final BooleanProperty ALLOW_FOR_DESCRIPTOR = new BooleanProperty("allowFor",
            "Allow assignment within the conditional expression of a for statement", false, 2.0f);

    private static final BooleanProperty ALLOW_WHILE_DESCRIPTOR = new BooleanProperty("allowWhile",
            "Allow assignment within the conditional expression of a while statement", false, 3.0f);

    private static final BooleanProperty ALLOW_INCREMENT_DECREMENT_DESCRIPTOR = new BooleanProperty(
            "allowIncrementDecrement",
            "Allow increment or decrement operators within the conditional expression of an if, for, or while statement",
            false, 4.0f);

    public AssignmentInOperandRule() {
        definePropertyDescriptor(ALLOW_IF_DESCRIPTOR);
        definePropertyDescriptor(ALLOW_FOR_DESCRIPTOR);
        definePropertyDescriptor(ALLOW_WHILE_DESCRIPTOR);
        definePropertyDescriptor(ALLOW_INCREMENT_DECREMENT_DESCRIPTOR);
    }

    @Override
    public Object visit(ASTExpression node, Object data) {
        Node parent = node.jjtGetParent();
        if ((parent instanceof ASTIfStatement && !getProperty(ALLOW_IF_DESCRIPTOR)
                || parent instanceof ASTWhileStatement && !getProperty(ALLOW_WHILE_DESCRIPTOR)
                || parent instanceof ASTForStatement && parent.jjtGetChild(1) == node
                        && !getProperty(ALLOW_FOR_DESCRIPTOR))
                && (node.hasDescendantOfType(ASTAssignmentOperator.class)
                        || !getProperty(ALLOW_INCREMENT_DECREMENT_DESCRIPTOR)
                                && (node.hasDescendantOfAnyType(ASTPreIncrementExpression.class,
                                                                ASTPreDecrementExpression.class, ASTPostfixExpression.class)))) {

            addViolation(data, node);
            return data;
        }
        return super.visit(node, data);
    }

    public boolean allowsAllAssignments() {
        return getProperty(ALLOW_IF_DESCRIPTOR) && getProperty(ALLOW_FOR_DESCRIPTOR)
                && getProperty(ALLOW_WHILE_DESCRIPTOR) && getProperty(ALLOW_INCREMENT_DECREMENT_DESCRIPTOR);
    }

    /**
     * @see PropertySource#dysfunctionReason()
     */
    @Override
    public String dysfunctionReason() {
        return allowsAllAssignments() ? "All assignment types allowed, no checks performed" : null;
    }
}
