<?xml version="1.0" encoding="UTF-8"?>
<test-data
    xmlns="http://pmd.sourceforge.net/rule-tests"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://pmd.sourceforge.net/rule-tests http://pmd.sourceforge.net/rule-tests_1_0_0.xsd">

    <code-fragment id="full-example"><![CDATA[
import my.Structure;
import my.DataFlowHandler;
import my.JavaNode;
import my.Linker;
import my.NodeType;
import my.Node;
import my.ASTStatementExpression;
import my.ASTVariableDeclarator;
import my.ASTExpression;
import my.ASTWhileStatement;
import my.ASTIfStatement;
import my.ASTForStatement;
import my.ASTSwitchStatement;
import my.ASTDoStatement;
import my.ASTAssertStatement;

import java.util.logging.Logger;
import java.util.logging.Level;
public class StatementAndBraceFinder {
    // total: 16 for sum of methods        + 1 for        vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
    //      = 17
    private static final Logger LOGGER = Logger.getLogger(StatementAndBraceFinder.class.getName());

    private final DataFlowHandler dataFlowHandler;
    private Structure dataFlow;

    public StatementAndBraceFinder(DataFlowHandler dataFlowHandler) {//0
        this.dataFlowHandler = dataFlowHandler;
    }

    public void buildDataFlowFor(JavaNode node) {//4
        this.dataFlow = new Structure(dataFlowHandler);
        this.dataFlow.createStartNode(node.getBeginLine()); // +1
        this.dataFlow.createNewNode(node);

        node.acceptVisitor(this, dataFlow);

        this.dataFlow.createEndNode(node.getEndLine()); // +1
        if (LOGGER.isLoggable(Level.FINE)) {
            // TODO SRT Remove after development
            LOGGER.fine("DataFlow is " + this.dataFlow.dump());
        }
        Linker linker = new Linker(dataFlowHandler,
                                   dataFlow.getBraceStack(), // +1
                                   dataFlow.getContinueBreakReturnStack()); // +1
    }


    private void tryToLog(String tag, NodeType type, Node node) {//2
        if (LOGGER.isLoggable(Level.FINEST)) {
            LOGGER.finest("pushOnStack " + tag + " " + type + ": line " + node.getBeginLine() // +1
                + ", column " + node.getBeginColumn());//+1
        }
    }

    private void tryToLog(NodeType type, Node node) { //0
        tryToLog("", type, node);
    }

    public Object visit(ASTStatementExpression node, Object data) { //2
        if (!(data instanceof Structure)) {
            return data;
        }
        Structure dataFlow = (Structure) data;
        if (LOGGER.isLoggable(Level.FINEST)) {
            LOGGER.finest("" + node.getBeginLine() + ", column " //+1
                + node.getBeginColumn()); //+1
        }
        dataFlow.createNewNode(node);
        return super.visit(node, data);
    }

    @Override
    public Object visit(ASTVariableDeclarator node, Object data) { //2
        Structure dataFlow = (Structure) data;
        if (LOGGER.isLoggable(Level.FINEST)) {
            LOGGER.finest("" + node.getBeginLine() + ", column " //+1
                                  + node.getBeginColumn()); //+1
        }
        dataFlow.createNewNode(node);
        return super.visit(node, data);
    }

    @Override
    public Object visit(ASTExpression node, Object data) {//6
        Structure dataFlow = (Structure) data;

        String loggerTag = "parent";

        Node parent = node.jjtGetParent();

        // TODO what about throw stmts?
        if (parent instanceof ASTIfStatement) {
            dataFlow.createNewNode(node); // START IF
            dataFlow.pushOnStack(NodeType.IF_EXPR, dataFlow.getLast());    // +1
            tryToLog(loggerTag, NodeType.IF_EXPR, node);
        } else if (parent instanceof ASTWhileStatement) {
            dataFlow.createNewNode(node); // START WHILE
            dataFlow.pushOnStack(NodeType.WHILE_EXPR, dataFlow.getLast());    // +1
            tryToLog(loggerTag, NodeType.WHILE_EXPR, node);
        } else if (parent instanceof ASTSwitchStatement) {
            dataFlow.createNewNode(node); // START SWITCH
            dataFlow.pushOnStack(NodeType.SWITCH_START, dataFlow.getLast());    // +1
            tryToLog(loggerTag, NodeType.SWITCH_START, node);
        } else if (parent instanceof ASTForStatement) {
            dataFlow.createNewNode(node); // FOR EXPR
            dataFlow.pushOnStack(NodeType.FOR_EXPR, dataFlow.getLast());    // +1
            tryToLog(loggerTag, NodeType.FOR_EXPR, node);
        } else if (parent instanceof ASTDoStatement) {
            dataFlow.createNewNode(node); // DO EXPR
            dataFlow.pushOnStack(NodeType.DO_EXPR, dataFlow.getLast());    // +1
            tryToLog(loggerTag, NodeType.DO_EXPR, node);
        } else if (parent instanceof ASTAssertStatement) {
            dataFlow.createNewNode(node);
            dataFlow.pushOnStack(NodeType.ASSERT_STATEMENT, dataFlow.getLast());    // +1
            tryToLog(loggerTag, NodeType.ASSERT_STATEMENT, node);
        }

        return super.visit(node, data);
    }

}
    ]]></code-fragment>


    <test-code>
        <description>Full example</description>
        <expected-problems>8</expected-problems>
        <expected-messages>
            <message>'StatementAndBraceFinder' has value 17.</message>
            <message>'StatementAndBraceFinder#StatementAndBraceFinder(DataFlowHandler)' has value 0.</message>
            <message>'StatementAndBraceFinder#buildDataFlowFor(JavaNode)' has value 4.</message>
            <message>'StatementAndBraceFinder#tryToLog(String, NodeType, Node)' has value 2.</message>
            <message>'StatementAndBraceFinder#tryToLog(NodeType, Node)' has value 0.</message>
            <message>'StatementAndBraceFinder#visit(ASTStatementExpression, Object)' has value 2.</message>
            <message>'StatementAndBraceFinder#visit(ASTVariableDeclarator, Object)' has value 2.</message>
            <message>'StatementAndBraceFinder#visit(ASTExpression, Object)' has value 6.</message>
        </expected-messages>
        <code-ref id="full-example"/>
    </test-code>

    <test-code disabled="true">
        <description>TODO: known limitation, should report 1</description>
        <!-- <rule-property name="reportClasses">false</rule-property> -->
        <expected-problems>1</expected-problems>
        <expected-messages>
            <message>'Foo#bar()' has value 1.</message>
        </expected-messages>
        <code><![CDATA[
public class Foo {
    Type a;
    void bar() {
        a.b.foo();
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>Test empty class</description>
        <expected-problems>1</expected-problems>
        <expected-messages>
            <message>'Foo' has value 0.</message>
        </expected-messages>
        <code><![CDATA[
public class Foo {
}
        ]]></code>
    </test-code>

    <test-code>
        <description>ATFD with interfaces or annotations</description>
        <expected-problems>2</expected-problems>
        <expected-messages>
            <message>'Foo' has value 0.</message>
            <message>'Foo$Tag' has value 0.</message>
        </expected-messages>
        <code><![CDATA[
public interface Foo {

    public final int h;

    @interface Tag {
        public static final int num = 0;
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>ATFD without method call chains</description>
        <expected-problems>2</expected-problems>
        <expected-messages>
            <message>'Foo' has value 2.</message>
            <message>'Foo#bar()' has value 2.</message>
        </expected-messages>
        <code><![CDATA[
import my.JFileChooser;
import my.FileFilter;
public class Foo {
    public void bar() {
        JFileChooser openDialog = createOpenFileChooser();
        FileFilter tmp = openDialog.getFileFilter();
        showStatus("Not a valid file format: " + tmp.getDescription());
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>ATFD with method call chains</description>
        <expected-problems>2</expected-problems>
        <expected-messages>
            <message>'Foo' has value 2.</message>
            <message>'Foo#bar()' has value 2.</message>
        </expected-messages>
        <code><![CDATA[
import my.JFileChooser;
public class Foo {
    public void bar() {
        JFileChooser openDialog = createOpenFileChooser();
        showStatus("Not a valid file format: " + openDialog.getFileFilter().getDescription());
    }
}
        ]]></code>
    </test-code>
    <test-code>
        <description>ATFD discounts static fields</description>
        <expected-problems>2</expected-problems>
        <expected-messages>
            <message>'Foo' has value 0.</message>
            <message>'Foo#bar()' has value 0.</message>
        </expected-messages>
        <code><![CDATA[
            import java.util.logging.Level;
            public class Foo {
                public void bar() {
                    Level.FINE.clone();
                }
            }
            ]]></code>
    </test-code>
</test-data>
