class PushdownNegationVisitor extends BaseVisitor {

    public static JexlNode applyDeMorgans(JexlNode root, boolean negateRoot) {
        if (root == null) {
            throw new IllegalArgumentException("root must not be null");
        }
        
        if (root.jjtGetNumChildren() == 0) {
            throw new IllegalStateException("root must have at least one child");
        }
        
        // build the child list, while negating each child
        List<JexlNode> negatedChildren = new ArrayList<>(root.jjtGetNumChildren());
        for (int i = 0; i < root.jjtGetNumChildren(); i++) {
            negatedChildren.add(JexlNodes.negate(root.jjtGetChild(i)));
        }
        
        // flip the root operator and finish validating the root
        JexlNode newRoot;
        if (root instanceof ASTAndNode) {
            newRoot = JexlNodeFactory.createOrNode(negatedChildren);
        } else if (root instanceof ASTOrNode) {
            newRoot = JexlNodeFactory.createAndNode(negatedChildren);
        } else {
            throw new IllegalArgumentException("root must be either an ASTAndNode or ASTOrNode");
        }
        
        if (negateRoot) {
            // negate the new node
            newRoot = JexlNodes.negate(newRoot);
        }
        
        // if there is a parent to swap
        if (root.jjtGetParent() != null) {
            // replace root with the newly negated node, AND -> OR or OR -> AND
            JexlNodes.swap(root.jjtGetParent(), root, newRoot);
        }
        
        return newRoot;
    }

}